Reworked and fixed issues with savePgn feature

SavePgn
Justin 2025-06-23 21:20:30 +02:00
commit a669a4b2ff
6 changed files with 207 additions and 17 deletions

View File

@ -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.PieceDTO;
import de.hs_mannheim.informatik.chess.model.BoardDTO; import de.hs_mannheim.informatik.chess.model.BoardDTO;
import de.hs_mannheim.informatik.chess.view.GameGui; import de.hs_mannheim.informatik.chess.view.GameGui;
import de.hs_mannheim.informatik.chess.view.MainGui;
public class GameController { public class GameController {
GameGui gui; GameGui gui;
ChessEngine engine; ChessEngine engine;
GameEndCallback callback;
private boolean gameOver = false;
private int selectedRow = -1, selectedCol = -1; private int selectedRow = -1, selectedCol = -1;
private List<int[]> highlightedFields = new ArrayList<>(); private List<int[]> highlightedFields = new ArrayList<>();
public GameController(GameGui gui, ChessEngine engine) { public GameController(GameGui gui, ChessEngine engine, GameEndCallback callback) {
this.gui = gui; this.gui = gui;
this.engine = engine; this.engine = engine;
this.callback = callback;
engine.initTimers(3, 0);
time();
initListeners(); initListeners();
updateGuiBoard(); 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) { private int flipRow(int row) {
return gui.isFlipped() ? 7 - row : row; return gui.isFlipped() ? 7 - row : row;
} }
@ -114,6 +127,9 @@ public class GameController {
} }
private void handleClick(int guiRow, int guiCol) { private void handleClick(int guiRow, int guiCol) {
if (gameOver) return;
int modelRow = flipRow(guiRow); int modelRow = flipRow(guiRow);
int modelCol = flipCol(guiCol); int modelCol = flipCol(guiCol);
@ -159,6 +175,9 @@ public class GameController {
} }
private void handleMove(MoveDTO move) { private void handleMove(MoveDTO move) {
if (gameOver) return;
BoardDTO boardDTO = engine.getBoardAsDTO(); BoardDTO boardDTO = engine.getBoardAsDTO();
PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()]; PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()];
boolean isPawn = piece != null && piece.getType().equals("PAWN"); boolean isPawn = piece != null && piece.getType().equals("PAWN");
@ -190,8 +209,16 @@ public class GameController {
if (engine.isMated()) { if (engine.isMated()) {
String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß";
gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); gui.displayMessage(winner + " hat gewonnen (Schachmatt)!");
gameOver = true;
askForRestart();
} else if (engine.isStalemate() || engine.isDraw()) { } else if (engine.isStalemate() || engine.isDraw()) {
gui.displayMessage("Remis! (Stalemate oder andere Regel)"); 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(); BoardDTO board = engine.getBoardAsDTO();
gui.updateBoard(board); 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 // Hilfsmethode, um von Koordinaten (row/col) auf z.B. "E2" zu kommen
private String coordToChessNotation(int modelRow, int modelCol) { private String coordToChessNotation(int modelRow, int modelCol) {
char file = (char)('A' + modelCol); char file = (char)('A' + modelCol);
int rank = 8 - modelRow; int rank = 8 - modelRow;
return "" + file + rank; 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) { private void resetFieldBackground(int row, int col) {
Color LIGHT = new Color(0xe0e1dd); Color LIGHT = new Color(0xe0e1dd);
Color DARK = new Color(0x778da9); Color DARK = new Color(0x778da9);

View File

@ -0,0 +1,6 @@
package de.hs_mannheim.informatik.chess.controller;
public interface GameEndCallback {
void onNewGameRequested();
void onReturnToMenu();
}

View File

@ -31,7 +31,15 @@ public class MainController {
mainGui.close(); mainGui.close();
GameGui gameGui = new GameGui(); GameGui gameGui = new GameGui();
ChessEngine engine = new ChessEngine(); 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() { private void startCreativeMode() {
@ -40,7 +48,7 @@ public class MainController {
ChessEngine engine = new ChessEngine(); ChessEngine engine = new ChessEngine();
new CreativeController(creativegui, engine); new CreativeController(creativegui, engine);
} }
private void startLoadGameMode() { private void startLoadGameMode() {
JFileChooser chooser = new JFileChooser(); JFileChooser chooser = new JFileChooser();
int result = chooser.showOpenDialog(null); int result = chooser.showOpenDialog(null);

View File

@ -28,6 +28,8 @@ public class ChessEngine {
private static final Logger logger = Logger.getLogger(ChessEngine.class.getName()); private static final Logger logger = Logger.getLogger(ChessEngine.class.getName());
private int currentMoveIndex = 0; private int currentMoveIndex = 0;
private Timer whiteTimer;
private Timer blackTimer;
public ChessEngine() { public ChessEngine() {
logging(); logging();
@ -245,6 +247,11 @@ public class ChessEngine {
return games; 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) { public void saveAsPgn(Game game, String path, String dateiname) {
// Sicher alle Strings holen (nie null) // Sicher alle Strings holen (nie null)
String event = safe(game.getRound().getEvent().getName()); String event = safe(game.getRound().getEvent().getName());
@ -362,4 +369,7 @@ public class ChessEngine {
} }
public Timer getWhiteTimer() { return whiteTimer; }
public Timer getBlackTimer() { return blackTimer; }
} }

View File

@ -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<Integer> onTick;
public Timer(int minutes, int seconds) {
this.secondsLeft = minutes * 60 + seconds;
}
public void setOnTimeout(Runnable onTimeout) {
this.onTimeout = onTimeout;
}
public void setOnTick(Consumer<Integer> 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;
}
}

View File

@ -27,12 +27,15 @@ public class GameGui {
private JLabel[][] fields = new JLabel[8][8]; private JLabel[][] fields = new JLabel[8][8];
private JButton flipBoardButton; private JButton flipBoardButton;
private boolean isFlipped = false; private boolean isFlipped = false;
JButton btnSave = new JButton("💾");
private JLabel whiteTimerLabel;
private JLabel blackTimerLabel;
JButton btnFirst = new JButton("|<"); JButton btnFirst = new JButton("|<");
JButton btnPrev = new JButton("<"); JButton btnPrev = new JButton("<");
JButton btnNext = new JButton(">"); JButton btnNext = new JButton(">");
JButton btnLast = new JButton(">|"); JButton btnLast = new JButton(">|");
JButton btnSave = new JButton("SavePgn");
Color LIGHT = new Color(0xe0e1dd); Color LIGHT = new Color(0xe0e1dd);
Color DARK = new Color(0x778da9); Color DARK = new Color(0x778da9);
@ -161,6 +164,9 @@ public class GameGui {
JPanel statsPanel = new JPanel(new BorderLayout()); JPanel statsPanel = new JPanel(new BorderLayout());
statsPanel.setBackground(new Color(0x0d1b2a)); statsPanel.setBackground(new Color(0x0d1b2a));
// Panel für die Timer (NORD)
statsPanel.add(timerPanel(), BorderLayout.NORTH);
// Move-Liste // Move-Liste
moveListPanel = new JPanel(); moveListPanel = new JPanel();
moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS)); moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS));
@ -169,31 +175,48 @@ public class GameGui {
moveListScroll.setPreferredSize(new Dimension(250, 800)); moveListScroll.setPreferredSize(new Dimension(250, 800));
statsPanel.add(moveListScroll, BorderLayout.CENTER); statsPanel.add(moveListScroll, BorderLayout.CENTER);
// Button-Leiste // Button-Leiste (SÜD)
JPanel buttonPanel = new JPanel(); JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(new Color(0x0d1b2a)); buttonPanel.setBackground(new Color(0x0d1b2a));
// Grid oder Flow buttonPanel.setLayout(new GridLayout(1, 5, 10, 0)); // Jetzt 5 statt 4 Spalten!
buttonPanel.setLayout(new GridLayout(1, 4, 10, 0));
// Style (optional)
btnFirst.setBackground(new Color(0x212529)); btnFirst.setForeground(Color.WHITE); btnFirst.setBackground(new Color(0x212529)); btnFirst.setForeground(Color.WHITE);
btnPrev.setBackground(new Color(0x212529)); btnPrev.setForeground(Color.WHITE); btnPrev.setBackground(new Color(0x212529)); btnPrev.setForeground(Color.WHITE);
btnNext.setBackground(new Color(0x212529)); btnNext.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)); btnLast.setForeground(Color.WHITE);
btnLast.setBackground(new Color(0x212529)); btnSave.setForeground(Color.WHITE); btnSave.setBackground(new Color(0x218838)); btnSave.setForeground(Color.WHITE);
// Hinzufügen
buttonPanel.add(btnFirst); buttonPanel.add(btnFirst);
buttonPanel.add(btnPrev); buttonPanel.add(btnPrev);
buttonPanel.add(btnNext); buttonPanel.add(btnNext);
buttonPanel.add(btnLast); buttonPanel.add(btnLast);
buttonPanel.add(btnSave); buttonPanel.add(btnSave);
// Unten ins BorderLayout
statsPanel.add(buttonPanel, BorderLayout.SOUTH); statsPanel.add(buttonPanel, BorderLayout.SOUTH);
return statsPanel; 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) { public String showPromotionDialog(String color) {
String[] choices = {"Dame", "Turm", "Springer", "Läufer"}; String[] choices = {"Dame", "Turm", "Springer", "Läufer"};
@ -246,7 +269,7 @@ public class GameGui {
public JButton getBtnPrev() { return btnPrev; } public JButton getBtnPrev() { return btnPrev; }
public JButton getBtnNext() { return btnNext; } public JButton getBtnNext() { return btnNext; }
public JButton getBtnLast() { return btnLast; } public JButton getBtnLast() { return btnLast; }
public JButton getBtnSave() { return btnSave; } public JButton getBtnSave() { return btnSave; }
public void updateBoard(BoardDTO boardDTO) { public void updateBoard(BoardDTO boardDTO) {
PieceDTO[][] board = boardDTO.getBoard(); 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) { public void displayMessage(String msg) {
JOptionPane.showMessageDialog(null, msg); JOptionPane.showMessageDialog(null, msg);