diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java index 5641c2a..f3b0f7b 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java @@ -15,20 +15,33 @@ import de.hs_mannheim.informatik.chess.model.MoveDTO; import de.hs_mannheim.informatik.chess.model.PieceDTO; import de.hs_mannheim.informatik.chess.model.BoardDTO; import de.hs_mannheim.informatik.chess.view.GameGui; +import de.hs_mannheim.informatik.chess.view.MainGui; public class GameController { GameGui gui; ChessEngine engine; + GameEndCallback callback; + + private boolean gameOver = false; private int selectedRow = -1, selectedCol = -1; private List highlightedFields = new ArrayList<>(); - public GameController(GameGui gui, ChessEngine engine) { + public GameController(GameGui gui, ChessEngine engine, GameEndCallback callback) { this.gui = gui; this.engine = engine; + this.callback = callback; + engine.initTimers(3, 0); + time(); initListeners(); updateGuiBoard(); } - + private void time() { + engine.getWhiteTimer().setOnTick(secs -> gui.updateWhiteTimerLabel(secs)); + engine.getBlackTimer().setOnTick(secs -> gui.updateBlackTimerLabel(secs)); + engine.getWhiteTimer().setOnTimeout(() -> onTimeout("WHITE")); + engine.getBlackTimer().setOnTimeout(() -> onTimeout("BLACK")); + engine.getWhiteTimer().start(); + } private int flipRow(int row) { return gui.isFlipped() ? 7 - row : row; } @@ -114,6 +127,9 @@ public class GameController { } private void handleClick(int guiRow, int guiCol) { + + if (gameOver) return; + int modelRow = flipRow(guiRow); int modelCol = flipCol(guiCol); @@ -159,6 +175,9 @@ public class GameController { } private void handleMove(MoveDTO move) { + + if (gameOver) return; + BoardDTO boardDTO = engine.getBoardAsDTO(); PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()]; boolean isPawn = piece != null && piece.getType().equals("PAWN"); @@ -190,8 +209,16 @@ public class GameController { if (engine.isMated()) { String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); + gameOver = true; + askForRestart(); } else if (engine.isStalemate() || engine.isDraw()) { gui.displayMessage("Remis! (Stalemate oder andere Regel)"); + gameOver = true; + askForRestart(); + } + + if (!engine.isMated() && !engine.isStalemate() && !engine.isDraw()) { + switchTimers(); } } @@ -200,15 +227,52 @@ public class GameController { BoardDTO board = engine.getBoardAsDTO(); gui.updateBoard(board); } + + private void switchTimers() { + if (engine.getCurrentPlayer().equals("WHITE")) { + engine.getBlackTimer().stop(); + engine.getWhiteTimer().start(); + } else { + engine.getWhiteTimer().stop(); + engine.getBlackTimer().start(); + } + } + // Hilfsmethode, um von Koordinaten (row/col) auf z.B. "E2" zu kommen private String coordToChessNotation(int modelRow, int modelCol) { char file = (char)('A' + modelCol); int rank = 8 - modelRow; return "" + file + rank; } + - + // Timeout-Methode + private void onTimeout(String color) { + if (gameOver) return; // Doppelt hält besser + gameOver = true; + String winner = color.equals("WHITE") ? "SCHWARZ" : "WEIß"; + gui.displayMessage(winner + " hat durch Zeit gewonnen!"); + engine.getWhiteTimer().stop(); + engine.getBlackTimer().stop(); + askForRestart(); + } + + private void askForRestart() { + int answer = javax.swing.JOptionPane.showConfirmDialog( + null, + "Neue Partie starten?", + "Spiel beendet", + javax.swing.JOptionPane.YES_NO_OPTION + ); + javax.swing.SwingUtilities.getWindowAncestor(gui.getField(0, 0)).dispose(); + if (answer == javax.swing.JOptionPane.YES_OPTION) { + callback.onNewGameRequested(); + } else { + callback.onReturnToMenu(); + } + } + private void resetFieldBackground(int row, int col) { Color LIGHT = new Color(0xe0e1dd); Color DARK = new Color(0x778da9); diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameEndCallback.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameEndCallback.java new file mode 100644 index 0000000..daed890 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameEndCallback.java @@ -0,0 +1,6 @@ +package de.hs_mannheim.informatik.chess.controller; + +public interface GameEndCallback { + void onNewGameRequested(); + void onReturnToMenu(); +} diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java index 16110d7..5607a08 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java @@ -31,7 +31,15 @@ public class MainController { mainGui.close(); GameGui gameGui = new GameGui(); ChessEngine engine = new ChessEngine(); - new GameController(gameGui, engine); + GameEndCallback callback = new GameEndCallback() { + public void onNewGameRequested() { + startNormalMode(); + } + public void onReturnToMenu() { + new MainController(); + } + }; + new GameController(gameGui, engine, callback); } private void startCreativeMode() { @@ -40,7 +48,7 @@ public class MainController { ChessEngine engine = new ChessEngine(); new CreativeController(creativegui, engine); } - + private void startLoadGameMode() { JFileChooser chooser = new JFileChooser(); int result = chooser.showOpenDialog(null); diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/model/ChessEngine.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/ChessEngine.java index 480f6ff..d4d6a10 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/model/ChessEngine.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/ChessEngine.java @@ -28,6 +28,8 @@ public class ChessEngine { private static final Logger logger = Logger.getLogger(ChessEngine.class.getName()); private int currentMoveIndex = 0; + private Timer whiteTimer; + private Timer blackTimer; public ChessEngine() { logging(); @@ -262,6 +264,11 @@ public class ChessEngine { return games; } + public void initTimers(int min, int sec) { + whiteTimer = new Timer(min, sec); + blackTimer = new Timer(min, sec); + } + public void saveAsPgn(Game game, String path, String dateiname) { // Sicher alle Strings holen (nie null) String event = safe(game.getRound().getEvent().getName()); @@ -379,4 +386,7 @@ public class ChessEngine { } + public Timer getWhiteTimer() { return whiteTimer; } + public Timer getBlackTimer() { return blackTimer; } + } diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/model/Timer.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/Timer.java new file mode 100644 index 0000000..19441d6 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/Timer.java @@ -0,0 +1,61 @@ +package de.hs_mannheim.informatik.chess.model; +import java.util.function.Consumer; + +public class Timer { + private int secondsLeft; + private javax.swing.Timer swingTimer; + private Runnable onTimeout; + private Consumer onTick; + + + public Timer(int minutes, int seconds) { + this.secondsLeft = minutes * 60 + seconds; + } + + public void setOnTimeout(Runnable onTimeout) { + this.onTimeout = onTimeout; + } + + public void setOnTick(Consumer onTick) { + this.onTick = onTick; + } + + public void start() { + if (swingTimer != null && swingTimer.isRunning()) { + swingTimer.stop(); + } + swingTimer = new javax.swing.Timer(1000, e -> { + secondsLeft--; + if (onTick != null) { + onTick.accept(secondsLeft); + } + if (secondsLeft <= 0) { + stop(); + if (onTimeout != null) { + onTimeout.run(); + } + } + }); + swingTimer.start(); + } + + public void stop() { + if (swingTimer != null) { + swingTimer.stop(); + } + } + + public void reset(int minutes, int seconds) { + stop(); + this.secondsLeft = minutes * 60 + seconds; + if (onTick != null) { + onTick.accept(secondsLeft); + } + } + + public int getSecondsLeft() { + return secondsLeft; + } +} + + diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/GameGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/GameGui.java index e418b51..6574fde 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/GameGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/GameGui.java @@ -27,12 +27,15 @@ public class GameGui { private JLabel[][] fields = new JLabel[8][8]; private JButton flipBoardButton; private boolean isFlipped = false; + JButton btnSave = new JButton("💾"); + + private JLabel whiteTimerLabel; + private JLabel blackTimerLabel; JButton btnFirst = new JButton("|<"); JButton btnPrev = new JButton("<"); JButton btnNext = new JButton(">"); JButton btnLast = new JButton(">|"); - JButton btnSave = new JButton("SavePgn"); Color LIGHT = new Color(0xe0e1dd); Color DARK = new Color(0x778da9); @@ -161,6 +164,9 @@ public class GameGui { JPanel statsPanel = new JPanel(new BorderLayout()); statsPanel.setBackground(new Color(0x0d1b2a)); + // Panel für die Timer (NORD) + statsPanel.add(timerPanel(), BorderLayout.NORTH); + // Move-Liste moveListPanel = new JPanel(); moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS)); @@ -169,31 +175,48 @@ public class GameGui { moveListScroll.setPreferredSize(new Dimension(250, 800)); statsPanel.add(moveListScroll, BorderLayout.CENTER); - // Button-Leiste + // Button-Leiste (SÜD) JPanel buttonPanel = new JPanel(); buttonPanel.setBackground(new Color(0x0d1b2a)); - // Grid oder Flow - buttonPanel.setLayout(new GridLayout(1, 4, 10, 0)); - - // Style (optional) + buttonPanel.setLayout(new GridLayout(1, 5, 10, 0)); // Jetzt 5 statt 4 Spalten! btnFirst.setBackground(new Color(0x212529)); btnFirst.setForeground(Color.WHITE); btnPrev.setBackground(new Color(0x212529)); btnPrev.setForeground(Color.WHITE); btnNext.setBackground(new Color(0x212529)); btnNext.setForeground(Color.WHITE); btnLast.setBackground(new Color(0x212529)); btnLast.setForeground(Color.WHITE); - btnLast.setBackground(new Color(0x212529)); btnSave.setForeground(Color.WHITE); - - // Hinzufügen + btnSave.setBackground(new Color(0x218838)); btnSave.setForeground(Color.WHITE); buttonPanel.add(btnFirst); buttonPanel.add(btnPrev); buttonPanel.add(btnNext); buttonPanel.add(btnLast); buttonPanel.add(btnSave); - - // Unten ins BorderLayout statsPanel.add(buttonPanel, BorderLayout.SOUTH); return statsPanel; } + + + private JPanel timerPanel() { + JPanel panel = new JPanel(new GridLayout(2, 1)); + panel.setBackground(new Color(0x0d1b2a)); + + whiteTimerLabel = new JLabel("Weiß: 03:00", SwingConstants.CENTER); + blackTimerLabel = new JLabel("Schwarz: 03:00", SwingConstants.CENTER); + + whiteTimerLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + whiteTimerLabel.setForeground(Color.WHITE); + whiteTimerLabel.setBackground(new Color(0x1b263b)); + whiteTimerLabel.setOpaque(true); + + blackTimerLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); + blackTimerLabel.setForeground(Color.WHITE); + blackTimerLabel.setBackground(new Color(0x1b263b)); + blackTimerLabel.setOpaque(true); + + panel.add(whiteTimerLabel); + panel.add(blackTimerLabel); + + return panel; + } public String showPromotionDialog(String color) { String[] choices = {"Dame", "Turm", "Springer", "Läufer"}; @@ -246,7 +269,7 @@ public class GameGui { public JButton getBtnPrev() { return btnPrev; } public JButton getBtnNext() { return btnNext; } public JButton getBtnLast() { return btnLast; } - public JButton getBtnSave() { return btnSave; } + public JButton getBtnSave() { return btnSave; } public void updateBoard(BoardDTO boardDTO) { PieceDTO[][] board = boardDTO.getBoard(); @@ -268,6 +291,24 @@ public class GameGui { } } + public void updateWhiteTimerLabel(int secondsLeft) { + whiteTimerLabel.setText("Weiß: " + formatTime(secondsLeft)); + } + + public void updateBlackTimerLabel(int secondsLeft) { + blackTimerLabel.setText("Schwarz: " + formatTime(secondsLeft)); + } + + private String formatTime(int seconds) { + int min = seconds / 60; + int sec = seconds % 60; + return String.format("%02d:%02d", min, sec); + } + + // Optional: Getter, falls du direkt ran willst (braucht man aber fast nie) + public JLabel getWhiteTimerLabel() { return whiteTimerLabel; } + public JLabel getBlackTimerLabel() { return blackTimerLabel; } + public void displayMessage(String msg) { JOptionPane.showMessageDialog(null, msg);