diff --git a/src/main/java/de/deversmann/Main.java b/src/main/java/de/deversmann/Main.java index aef4018..cbcf0ad 100644 --- a/src/main/java/de/deversmann/Main.java +++ b/src/main/java/de/deversmann/Main.java @@ -2,6 +2,7 @@ package de.deversmann; public class Main { public static void main(String[] args) { - System.out.println("Hello, World!"); + de.deversmann.gui.HitoriApp app = new de.deversmann.gui.HitoriApp(); + app.start(); } } \ No newline at end of file diff --git a/src/main/java/de/deversmann/domain/HighscoreManager.java b/src/main/java/de/deversmann/domain/HighscoreManager.java index 42d67cc..c0146c7 100644 --- a/src/main/java/de/deversmann/domain/HighscoreManager.java +++ b/src/main/java/de/deversmann/domain/HighscoreManager.java @@ -4,72 +4,17 @@ import java.io.*; import java.util.*; public class HighscoreManager { - private final Map> highscoreMap; + private List highscores public HighscoreManager() { - highscoreMap = new HashMap<>(); + this.highscores = new ArrayList<>(); } - public void addHighscore(String spielfeld, String name, long zeit, int fehler) { - HighscoreEntry entry = new HighscoreEntry(name, zeit, fehler); - highscoreMap.computeIfAbsent(spielfeld, k -> new ArrayList<>()).add(entry); - saveHighscores(); + public void addHighscore(int score) { + highscores.add(score); } - public List getHighscores(String spielfeld) { - return highscoreMap.getOrDefault(spielfeld, new ArrayList<>()); + public List getHighscores() { + return new ArrayList<>(highscores); } - - public double getDurchschnittszeit(String spielfeld) { - List highscores = getHighscores(spielfeld); - return highscores.stream().mapToLong(HighscoreEntry::getZeit).average().orElse(0); - } - - private void saveHighscores() { - try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("highscores.dat"))) { - oos.writeObject(highscoreMap); - } catch (IOException e) { - System.err.println("Fehler beim Speichern der Highscores: " + e.getMessage()); - } - } - - @SuppressWarnings("unchecked") - public void loadHighscores() { - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("highscores.dat"))) { - Map> loadedMap = (Map>) ois.readObject(); - highscoreMap.clear(); - highscoreMap.putAll(loadedMap); - } catch (IOException | ClassNotFoundException e) { - System.err.println("Fehler beim Laden der Highscores: " + e.getMessage()); - } - } - - public static class HighscoreEntry implements Serializable { - private final String name; - private final long zeit; - private final int fehler; - - public HighscoreEntry(String name, long zeit, int fehler) { - this.name = name; - this.zeit = zeit; - this.fehler = fehler; - } - - public String getName() { - return name; - } - - public long getZeit() { - return zeit; - } - - public int getFehler() { - return fehler; - } - - @Override - public String toString() { - return name + ": " + zeit + "ms, Fehler: " + fehler; - } - } -} +} \ No newline at end of file diff --git a/src/main/java/de/deversmann/gui/GameView.java b/src/main/java/de/deversmann/gui/GameView.java new file mode 100644 index 0000000..a150fd4 --- /dev/null +++ b/src/main/java/de/deversmann/gui/GameView.java @@ -0,0 +1,235 @@ +package de.deversmann.gui; + +import de.deversmann.facade.Facade; + +import javax.swing.*; +import java.awt.*; +import java.io.IOException; +import java.util.List; +import java.util.Random; + +public class GameView extends JFrame { + + private final Facade facade; + private SpielfeldGUI_2_0 spielfeldGUI; + + private JComboBox cbSpielfelder; + private JButton btnLoad; + private JButton btnRandom; + private JButton btnCheckSolution; + private JButton btnShowSolution; + private JButton btnReset; + private JLabel timeLabel; + private Timer timer; + + private final String[] spielfelder = { + "4x4.csv", + "5x5.csv", + "8x8_leicht.csv", + "8x8_medium.csv", + "10x10.csv", + "15x15.csv" + }; + + public GameView(Facade facade) { + this.facade = facade; + setTitle("Hitori App"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(800, 700); + initComponents(); + startTimer(); + setVisible(true); + } + + private void initComponents() { + JPanel controlPanel = new JPanel(); + + cbSpielfelder = new JComboBox<>(spielfelder); + btnLoad = new JButton("Spielfeld laden"); + btnRandom = new JButton("Zufällig"); + btnCheckSolution = new JButton("Lösung prüfen"); + btnShowSolution = new JButton("Lösung anzeigen"); + btnReset = new JButton("Reset"); + + timeLabel = new JLabel("Time: 0s"); + + btnLoad.addActionListener(e -> { + String selectedFile = (String) cbSpielfelder.getSelectedItem(); + if (selectedFile != null) { + ladeSpielfeld("src/Spielfelder/" + selectedFile); + } + }); + + btnRandom.addActionListener(e -> { + Random rand = new Random(); + int idx = rand.nextInt(spielfelder.length); + String randomField = spielfelder[idx]; + cbSpielfelder.setSelectedItem(randomField); + ladeSpielfeld("src/Spielfelder/" + randomField); + }); + + btnCheckSolution.addActionListener(e -> checkSolution()); + btnShowSolution.addActionListener(e -> showSolution()); + btnReset.addActionListener(e -> resetField()); + + controlPanel.add(new JLabel("Wähle ein Spielfeld:")); + controlPanel.add(cbSpielfelder); + controlPanel.add(btnLoad); + controlPanel.add(btnRandom); + controlPanel.add(btnCheckSolution); + controlPanel.add(btnShowSolution); + controlPanel.add(btnReset); + controlPanel.add(timeLabel); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(controlPanel, BorderLayout.NORTH); + + spielfeldGUI = new SpielfeldGUI_2_0(1, 1); + getContentPane().add(spielfeldGUI, BorderLayout.CENTER); + } + + private void startTimer() { + timer = new Timer(1000, e -> { + long seconds = facade.getElapsedTimeSoFar(); + timeLabel.setText("Time: " + seconds + "s"); + }); + timer.start(); + } + + private void ladeSpielfeld(String pfad) { + try { + facade.ladeSpielfeld(pfad); + facade.loadCurrentPuzzleHighscoreFromFile(); + + int[][] feld = facade.getAktuellesPuzzle(); + if (feld != null) { + spielfeldGUI.updateGrid(feld); + timeLabel.setText("Time: 0s"); + } + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, + "Fehler beim Laden: " + ex.getMessage(), + "Fehler", + JOptionPane.ERROR_MESSAGE + ); + } + } + + private void checkSolution() { + var blackCells = spielfeldGUI.getBlackCells(); + var solutionCoordinates = facade.getLoesungsKoordinaten(); + if (solutionCoordinates == null) { + JOptionPane.showMessageDialog(this, "Kein Puzzle geladen!", "Fehler", JOptionPane.ERROR_MESSAGE); + return; + } + + Set solutionSet = new HashSet<>(); + for (int[] sol : solutionCoordinates) { + solutionSet.add((sol[0] - 1) + "," + (sol[1] - 1)); + } + + int errorsThisCheck = 0; + for (int[] bc : blackCells) { + String bcStr = bc[0] + "," + bc[1]; + if (!solutionSet.contains(bcStr)) { + spielfeldGUI.markCellAsError(bc[0], bc[1]); + facade.incrementErrorCount(); + errorsThisCheck++; + } + } + + if (errorsThisCheck > 0) { + JOptionPane.showMessageDialog(this, + "Noch nicht korrekt! " + errorsThisCheck + " falsche Felder markiert.", + "Ergebnis", + JOptionPane.WARNING_MESSAGE + ); + return; + } + + boolean allSet = true; + for (String sol : solutionSet) { + if (!containsCell(blackCells, sol)) { + allSet = false; + break; + } + } + if (!allSet) { + JOptionPane.showMessageDialog(this, + "Nicht alle richtigen Felder sind schwarz!", + "Ergebnis", + JOptionPane.WARNING_MESSAGE + ); + return; + } + + long finalTime = facade.stopZeiterfassung(); + timeLabel.setText("Time: " + finalTime + "s"); + + String playerName = JOptionPane.showInputDialog( + this, + "Gratulation, richtig gelöst!\nBitte Namen eingeben:", + "Name eingeben", + JOptionPane.QUESTION_MESSAGE + ); + if (playerName == null || playerName.isBlank()) { + playerName = "Unbekannt"; + } + + int totalErrors = facade.getCurrentErrorCount(); + facade.addHighscoreForCurrentPuzzle(playerName, finalTime, totalErrors); + + try { + facade.saveCurrentPuzzleHighscoreToFile(); + } catch (IOException e) { + System.out.println("Konnte Puzzle-spezifischen Highscore nicht speichern: " + e.getMessage()); + } + + double avgTime = facade.getAverageTimeForCurrentPuzzle(); + StringBuilder sb = new StringBuilder(); + sb.append("Spiel gelöst!\n"); + sb.append("Deine Zeit: ").append(finalTime).append("s\n"); + sb.append("Fehler: ").append(totalErrors).append("\n"); + if (avgTime < 0) { + sb.append("Durchschnittszeit: Keine Einträge\n"); + } else { + sb.append(String.format("Durchschnittszeit: %.2f s\n", avgTime)); + } + + JOptionPane.showMessageDialog(this, sb.toString(), "Ergebnis", JOptionPane.INFORMATION_MESSAGE); + } + + private boolean containsCell(List blackCells, String sol) { + String[] parts = sol.split(","); + int rr = Integer.parseInt(parts[0]); + int cc = Integer.parseInt(parts[1]); + for (int[] bc : blackCells) { + if (bc[0] == rr && bc[1] == cc) { + return true; + } + } + return false; + } + + private void showSolution() { + var feld = facade.getAktuellesPuzzle(); + if (feld == null) return; + spielfeldGUI.updateGrid(feld); + + var solution = facade.getLoesungsKoordinaten(); + if (solution != null) { + for (int[] coord : solution) { + spielfeldGUI.setCellBlack(coord[0] - 1, coord[1] - 1); + } + } + spielfeldGUI.setAllNonBlackToWhite(); + } + + private void resetField() { + var feld = facade.getAktuellesPuzzle(); + if (feld != null) { + spielfeldGUI.updateGrid(feld); + timeLabel.setText("Time: 0s"); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/deversmann/gui/HitoriApp.java b/src/main/java/de/deversmann/gui/HitoriApp.java new file mode 100644 index 0000000..87c226f --- /dev/null +++ b/src/main/java/de/deversmann/gui/HitoriApp.java @@ -0,0 +1,17 @@ +package de.deversmann.gui; + +import de.deversmann.facade.Facade; + +public class HitoriApp { + + private Facade facade; + + public HitoriApp() { + facade = new Facade(); + } + + public void start() { + GameView view = new GameView(facade); + view.setVisible(true); + } +} \ No newline at end of file diff --git a/src/main/java/de/deversmann/gui/SpielfeldGUI.java b/src/main/java/de/deversmann/gui/SpielfeldGUI.java index 1bea0b3..3b244cf 100644 --- a/src/main/java/de/deversmann/gui/SpielfeldGUI.java +++ b/src/main/java/de/deversmann/gui/SpielfeldGUI.java @@ -1,97 +1,135 @@ package de.deversmann.gui; -import de.deversmann.domain.Spielfeld; -import de.deversmann.domain.Zeiterfassung; - import javax.swing.*; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.*; -public class SpielfeldGUI { +public class SpielfeldGUI_2_0 extends JPanel { - private JFrame frame; - private JPanel spielfeldPanel; - private JLabel timerLabel; - private Timer guiTimer; - private Spielfeld spielfeld; - private Zeiterfassung timer; + private JLabel[][] grid; + private Set errorCells = new HashSet<>(); - public SpielfeldGUI() { - // Initialisiere Domain-Klassen - spielfeld = new Spielfeld(); - timer = new Zeiterfassung(); + public SpielfeldGUI_2_0(int rows, int cols) { + setLayout(new GridLayout(rows, cols)); + grid = new JLabel[rows][cols]; - // Erstelle das Hauptfenster - frame = new JFrame("Hitori Spielfeld"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(800, 600); - frame.setLayout(new BorderLayout()); - - // Timer-Anzeige - timerLabel = new JLabel("00:00:00.000"); - timerLabel.setFont(new Font("Monospaced", Font.BOLD, 16)); - frame.add(timerLabel, BorderLayout.NORTH); - - // Spielfeld-Panel - spielfeldPanel = new JPanel(); - frame.add(spielfeldPanel, BorderLayout.CENTER); - - // Button-Panel - JPanel buttonPanel = new JPanel(); - JButton selectButton = new JButton("Spielfeld auswählen"); - JButton startButton = new JButton("Timer starten"); - JButton stopButton = new JButton("Timer stoppen"); - - buttonPanel.add(selectButton); - buttonPanel.add(startButton); - buttonPanel.add(stopButton); - frame.add(buttonPanel, BorderLayout.SOUTH); - - // Aktionen für Buttons - selectButton.addActionListener(e -> ladeSpielfeldMitAuswahl()); - startButton.addActionListener(e -> starteTimer()); - stopButton.addActionListener(e -> stoppeTimer()); - - // Timer für GUI-Updates - guiTimer = new Timer(100, e -> timerLabel.setText(timer.getFormattedTime())); - - // Fenster sichtbar machen - frame.setVisible(true); + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + JLabel label = createCellLabel("-", r, c); + grid[r][c] = label; + add(label); + } + } } - private void starteTimer() { - timer.start(); - guiTimer.start(); + public void updateGrid(int[][] feld) { + if (feld == null) { + return; + } + removeAll(); + int rows = feld.length; + int cols = feld[0].length; + setLayout(new GridLayout(rows, cols)); + grid = new JLabel[rows][cols]; + errorCells.clear(); + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + String text = String.valueOf(feld[r][c]); + JLabel label = createCellLabel(text, r, c); + grid[r][c] = label; + add(label); + } + } + revalidate(); + repaint(); } - private void stoppeTimer() { - timer.stop(); - guiTimer.stop(); + public void setAllNonBlackToWhite() { + if (grid == null) return; + for (int r = 0; r < grid.length; r++) { + for (int c = 0; c < grid[r].length; c++) { + JLabel lbl = grid[r][c]; + if (lbl != null) { + if (!lbl.getBackground().equals(Color.BLACK)) { + lbl.setBackground(Color.WHITE); + lbl.setForeground(Color.BLACK); + clearErrorMark(r, c); + } + } + } + } } - private void ladeSpielfeldMitAuswahl() { - + public void markCellAsError(int row, int col) { + if (!isValidCell(row, col)) return; + errorCells.add(row + "," + col); + grid[row][col].setBorder(BorderFactory.createLineBorder(Color.RED, 3)); } - private void zeigeSpielfeld() { - + public void clearErrorMark(int row, int col) { + errorCells.remove(row + "," + col); + if (!isValidCell(row, col)) return; + grid[row][col].setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1)); } - private void feldAktion(int x, int y, JButton button) { - String aktuellerZustand = spielfeld.getZustand(x, y); - String neuerZustand = switch (aktuellerZustand) { - case "grau" -> "schwarz"; - case "schwarz" -> "weiß"; - default -> "grau"; - }; - spielfeld.setzeZustand(x, y, neuerZustand); - button.setBackground(switch (neuerZustand) { - case "schwarz" -> Color.BLACK; - case "weiß" -> Color.WHITE; - default -> Color.GRAY; + public List getBlackCells() { + List black = new ArrayList<>(); + if (grid == null) return black; + + for (int r = 0; r < grid.length; r++) { + for (int c = 0; c < grid[r].length; c++) { + JLabel lbl = grid[r][c]; + if (lbl != null && lbl.getBackground().equals(Color.BLACK)) { + black.add(new int[]{r, c}); + } + } + } + return black; + } + + public void setCellBlack(int row, int col) { + if (!isValidCell(row, col)) return; + JLabel lbl = grid[row][col]; + lbl.setBackground(Color.BLACK); + lbl.setForeground(Color.WHITE); + clearErrorMark(row, col); + } + + private JLabel createCellLabel(String text, int row, int col) { + JLabel label = new JLabel(text, SwingConstants.CENTER); + label.setOpaque(true); + label.setBackground(Color.GRAY); + label.setForeground(Color.BLACK); + label.setFont(new Font("Arial", Font.BOLD, 24)); + label.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1)); + + label.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + toggleColor(label, row, col); + } }); + return label; } - public static void main(String[] args) { - SwingUtilities.invokeLater(SpielfeldGUI::new); + private void toggleColor(JLabel label, int row, int col) { + if (label.getBackground().equals(Color.GRAY) || label.getBackground().equals(Color.WHITE)) { + label.setBackground(Color.BLACK); + label.setForeground(Color.WHITE); + } else { + label.setBackground(Color.WHITE); + label.setForeground(Color.BLACK); + } + if (errorCells.contains(row + "," + col)) { + clearErrorMark(row, col); + } } -} + + private boolean isValidCell(int r, int c) { + if (grid == null) return false; + return (r >= 0 && r < grid.length && c >= 0 && c < grid[r].length); + } +} \ No newline at end of file