From f72883710f4d48af9e93773946318a665f65e4c4 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 22 Jun 2025 23:02:28 +0200 Subject: [PATCH 01/32] New method showPromotionDialog in GameGui --- .../informatik/chess/view/GameGui.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) 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 76f4b97..5dfe8ef 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 @@ -112,7 +112,7 @@ public class GameGui { } else { label.setBackground(new Color(0xe0e1dd)); } - fields[row][col] = label; // <-- Save the label + fields[row][col] = label; boardPanel.add(label); } @@ -158,7 +158,7 @@ public class GameGui { JPanel statsPanel = new JPanel(new BorderLayout()); statsPanel.setBackground(new Color(0x0d1b2a)); - // Move-Liste (scrollbar, wie gehabt) + // Move-Liste moveListPanel = new JPanel(); moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS)); moveListPanel.setBackground(new Color(0x0d1b2a)); @@ -169,7 +169,7 @@ public class GameGui { // Button-Leiste JPanel buttonPanel = new JPanel(); buttonPanel.setBackground(new Color(0x0d1b2a)); - // Grid oder Flow – je nach Geschmack + // Grid oder Flow buttonPanel.setLayout(new GridLayout(1, 4, 10, 0)); // Style (optional) @@ -190,6 +190,24 @@ public class GameGui { return statsPanel; } + public String showPromotionDialog(String color) { + String[] choices = {"Dame", "Turm", "Springer", "Läufer"}; + String choice = (String) JOptionPane.showInputDialog( + null, + color + "er Bauer wird umgewandelt – wähle Figur:", + "Bauernumwandlung", + JOptionPane.PLAIN_MESSAGE, + null, + choices, + choices[0]); + if (choice == null) return "QUEEN"; + switch (choice) { + case "Turm": return "ROOK"; + case "Springer": return "KNIGHT"; + case "Läufer": return "BISHOP"; + default: return "QUEEN"; + } + } public void updateMoveList(List moves) { moveListPanel.removeAll(); From 31a917d586a891b2e198321f2974d55f95e0fe31 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 22 Jun 2025 23:04:20 +0200 Subject: [PATCH 02/32] New method moveWithPromotion implemented in ChessEngine --- .../informatik/chess/model/ChessEngine.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) 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 e7d3c9f..4207c6b 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 @@ -87,6 +87,37 @@ public class ChessEngine { return convertPieceToDTO(piece); } + public boolean moveWithPromotion(MoveDTO move, String promotionPiece) { + String from = "" + (char)('A' + move.getFromCol()) + (8 - move.getFromRow()); + String to = "" + (char)('A' + move.getToCol()) + (8 - move.getToRow()); + + // Die Farbe bestimmen! + boolean isWhite = (8 - move.getFromRow()) < (8 - move.getToRow()); + Piece promotion; + switch (promotionPiece) { + case "ROOK": promotion = isWhite ? Piece.WHITE_ROOK : Piece.BLACK_ROOK; break; + case "KNIGHT": promotion = isWhite ? Piece.WHITE_KNIGHT : Piece.BLACK_KNIGHT; break; + case "BISHOP": promotion = isWhite ? Piece.WHITE_BISHOP : Piece.BLACK_BISHOP; break; + default: promotion = isWhite ? Piece.WHITE_QUEEN : Piece.BLACK_QUEEN; + } + + Move libMove = new Move(Square.valueOf(from), Square.valueOf(to), promotion); + + if (board.legalMoves().contains(libMove)) { + board.doMove(libMove); + + if (currentMoveIndex < moves.size()) { + moves = new ArrayList<>(moves.subList(0, currentMoveIndex)); + } + moves.add(libMove); + currentMoveIndex++; + logger.info("Promotionszug durchgeführt: " + libMove); + return true; + } + logger.warning("Ungültiger Promotionszug: " + libMove); + return false; + } + public BoardDTO getBoardAsDTO() { logger.info("Erstelle DTO-Abbild des Boards"); PieceDTO[][] dtoBoard = new PieceDTO[8][8]; From dac360d411c88b526192e45f8237a51987ed0159 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 22 Jun 2025 23:13:07 +0200 Subject: [PATCH 03/32] Reworked handleMove for pawn promotion feature --- .../chess/controller/Controller.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java index 162df94..70ee296 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java @@ -137,20 +137,29 @@ public class Controller { } } - public void handleMove(MoveDTO move) { + private void handleMove(MoveDTO move) { + BoardDTO boardDTO = engine.getBoardAsDTO(); + PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()]; + boolean isPawn = piece != null && piece.getType().equals("PAWN"); + boolean isWhitePromotion = isPawn && piece.getColor().equals("WHITE") && move.getToRow() == 0; + boolean isBlackPromotion = isPawn && piece.getColor().equals("BLACK") && move.getToRow() == 7; + + if (isWhitePromotion || isBlackPromotion) { + String color = piece.getColor().equals("WHITE") ? "Weiß" : "Schwarz"; + String promotion = gui.showPromotionDialog(color); + if (engine.moveWithPromotion(move, promotion)) { + updateGuiBoard(); + gui.updateMoveList(engine.getMoveListStringsGrouped()); + } else { + gui.displayMessage("Ungültiger Promotionszug!"); + } + return; + } + + // Normale Züge wie gehabt: if (engine.move(move)) { updateGuiBoard(); - - //Züge in der MoveList aktualisieren gui.updateMoveList(engine.getMoveListStringsGrouped()); - - //Spielstatus prüfen - if (engine.isMated()) { - String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; - gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); - } else if (engine.isStalemate() || engine.isDraw()) { - gui.displayMessage("Remis! (Stalemate oder andere Regel)"); - } } else { gui.displayMessage("Ungültiger Zug!"); } From d1feea3fe511267bd89f0adedb3dddb53ebcb191 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 22 Jun 2025 23:48:40 +0200 Subject: [PATCH 04/32] Changed name Controller to GameController --- .../chess/controller/{Controller.java => GameController.java} | 4 ++-- .../main/java/de/hs_mannheim/informatik/chess/main/Main.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename schach/src/main/java/de/hs_mannheim/informatik/chess/controller/{Controller.java => GameController.java} (96%) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java similarity index 96% rename from schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java rename to schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java index 70ee296..36eaabd 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/Controller.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameController.java @@ -14,13 +14,13 @@ import de.hs_mannheim.informatik.chess.model.PieceDTO; import de.hs_mannheim.informatik.chess.model.BoardDTO; import de.hs_mannheim.informatik.chess.view.GameGui; -public class Controller { +public class GameController { GameGui gui; ChessEngine engine; private int selectedRow = -1, selectedCol = -1; private List highlightedFields = new ArrayList<>(); - public Controller(GameGui gui, ChessEngine engine) { + public GameController(GameGui gui, ChessEngine engine) { this.gui = gui; this.engine = engine; initListeners(); diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java index 94d5753..24e5fa1 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java @@ -1,5 +1,5 @@ package de.hs_mannheim.informatik.chess.main; -import de.hs_mannheim.informatik.chess.controller.Controller; +import de.hs_mannheim.informatik.chess.controller.GameController; import de.hs_mannheim.informatik.chess.model.ChessEngine; import de.hs_mannheim.informatik.chess.view.GameGui; import de.hs_mannheim.informatik.chess.view.MainGui; @@ -12,7 +12,7 @@ public class Main{ //Wenn "Normal Modus" gedrückt wird GameGui gui = new GameGui(); ChessEngine engine = new ChessEngine(); - new Controller(gui, engine); + new GameController(gui, engine); }); } From bfbb87864741406ea3b0f2670e1142d9d24aee06 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:04:13 +0200 Subject: [PATCH 05/32] Removed unused buttons in MainGui --- .../informatik/chess/view/MainGui.java | 44 ++++++++----------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java index f770a47..4e40418 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java @@ -1,15 +1,19 @@ package de.hs_mannheim.informatik.chess.view; import java.awt.*; +import java.awt.event.ActionListener; + import javax.swing.*; public class MainGui { private JFrame frame; - private Runnable onStartGame; - public MainGui(Runnable onStartGame) { - this.onStartGame = onStartGame; + JButton btnMode1 = new JButton("Normal Mode"); + JButton btnCreative = new JButton("Creative Mode"); + JButton btnLoadGame = new JButton("Load Game (PGN)"); + + public MainGui() { frame = new JFrame("Chess - Hauptmenü"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -38,44 +42,32 @@ public class MainGui { gbc.ipady = 15; mainPanel.add(Box.createRigidArea(new Dimension(0, 20)), gbc); - //Buttons - JButton btnMode1 = new JButton("Normal Mode"); - JButton btnMode2 = new JButton("Mode 2 (coming soon)"); - JButton btnMode3 = new JButton("Mode 3 (coming soon)"); - JButton btnCreative = new JButton("Creative Mode"); - JButton btnLoadGame = new JButton("Load Game (PGN)"); - styleButton(btnMode1); - styleButton(btnMode2); - styleButton(btnMode3); styleButton(btnCreative); styleButton(btnLoadGame); - gbc.gridy = 2; gbc.ipady = 25; + gbc.gridy = 1; gbc.ipady = 25; mainPanel.add(btnMode1, gbc); - - gbc.gridy = 3; - mainPanel.add(btnMode2, gbc); - - gbc.gridy = 4; - mainPanel.add(btnMode3, gbc); - gbc.gridy = 5; + gbc.gridy = 2; mainPanel.add(btnCreative, gbc); - gbc.gridy = 6; + gbc.gridy = 3; mainPanel.add(btnLoadGame, gbc); frame.add(mainPanel); frame.setVisible(true); - //Button ActionListener für "Normal Modus" - btnMode1.addActionListener(e -> { - frame.dispose(); // Hauptmenü schließen - onStartGame.run(); // **Ruft den Callback auf** - }); } + // --- Methoden für Controller zum Setzen der Listener --- + public void setNormalModeListener(ActionListener l) { btnMode1.addActionListener(l); } + public void setCreativeModeListener(ActionListener l) { btnCreative.addActionListener(l); } + public void setLoadGameListener(ActionListener l) { btnLoadGame.addActionListener(l); } + public void close() { + frame.dispose(); + } + // Helper für Button Styling private void styleButton(JButton btn) { btn.setFont(new Font("Serif", Font.BOLD, 30)); From 519851f46ba4d7914368e05dfc0d4f2c7d0dc4ee Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:04:50 +0200 Subject: [PATCH 06/32] Reworked MainController --- .../chess/controller/MainController.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java 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 new file mode 100644 index 0000000..01e0212 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/MainController.java @@ -0,0 +1,34 @@ +package de.hs_mannheim.informatik.chess.controller; + +import de.hs_mannheim.informatik.chess.view.MainGui; +import de.hs_mannheim.informatik.chess.view.GameGui; +import de.hs_mannheim.informatik.chess.model.ChessEngine; + +public class MainController { + private MainGui mainGui; + + public MainController() { + mainGui = new MainGui(); + + mainGui.setNormalModeListener(e -> startNormalMode()); + mainGui.setCreativeModeListener(e -> startCreativeMode()); + mainGui.setLoadGameListener(e -> startLoadGameMode()); + } + + private void startNormalMode() { + mainGui.close(); + GameGui gameGui = new GameGui(); + ChessEngine engine = new ChessEngine(); + new GameController(gameGui, engine); + } + + private void startCreativeMode() { + mainGui.close(); + // Hier z.B. CreativeSetupGui öffnen (siehe oben) + } + + private void startLoadGameMode() { + mainGui.close(); + // Hier LoadGame-Logik + } +} \ No newline at end of file From 485b3d6e142efff9a42a0dfce6f67004170d2c76 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:08:48 +0200 Subject: [PATCH 07/32] Initializing new MainController in Main --- .../de/hs_mannheim/informatik/chess/main/Main.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java index 24e5fa1..392bca0 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/main/Main.java @@ -1,19 +1,12 @@ package de.hs_mannheim.informatik.chess.main; -import de.hs_mannheim.informatik.chess.controller.GameController; -import de.hs_mannheim.informatik.chess.model.ChessEngine; -import de.hs_mannheim.informatik.chess.view.GameGui; -import de.hs_mannheim.informatik.chess.view.MainGui; +import de.hs_mannheim.informatik.chess.controller.MainController; + public class Main{ public static void main( String[] args ){ - new MainGui(() -> { - //Wenn "Normal Modus" gedrückt wird - GameGui gui = new GameGui(); - ChessEngine engine = new ChessEngine(); - new GameController(gui, engine); - }); + new MainController(); } } From fcab410a2f6ccbbb7ce74d6c5c778265c331afda Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:22:13 +0200 Subject: [PATCH 08/32] New CreativeGui class --- .../de/hs_mannheim/informatik/chess/view/CreativeGui.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java new file mode 100644 index 0000000..56b0582 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java @@ -0,0 +1,5 @@ +package de.hs_mannheim.informatik.chess.view; + +public class CreativeGui { + +} From 532059ba0bcb4c6068559681aed3fd97139966de Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:22:43 +0200 Subject: [PATCH 09/32] New PgnGui class --- .../java/de/hs_mannheim/informatik/chess/view/PgnGui.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java new file mode 100644 index 0000000..459b893 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java @@ -0,0 +1,5 @@ +package de.hs_mannheim.informatik.chess.view; + +public class PgnGui { + +} From 4fe9a8ad8a779f4b780f88d12c2482e14682eb12 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 00:48:30 +0200 Subject: [PATCH 10/32] Fixed wrong chessboard square colors --- .../chess/controller/GameController.java | 39 ++++++---- .../informatik/chess/view/GameGui.java | 74 ++++++++++--------- 2 files changed, 64 insertions(+), 49 deletions(-) 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 36eaabd..c0b4d56 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 @@ -144,27 +144,37 @@ public class GameController { boolean isWhitePromotion = isPawn && piece.getColor().equals("WHITE") && move.getToRow() == 0; boolean isBlackPromotion = isPawn && piece.getColor().equals("BLACK") && move.getToRow() == 7; + boolean success = false; + if (isWhitePromotion || isBlackPromotion) { String color = piece.getColor().equals("WHITE") ? "Weiß" : "Schwarz"; String promotion = gui.showPromotionDialog(color); - if (engine.moveWithPromotion(move, promotion)) { - updateGuiBoard(); - gui.updateMoveList(engine.getMoveListStringsGrouped()); - } else { + success = engine.moveWithPromotion(move, promotion); + if (!success) { gui.displayMessage("Ungültiger Promotionszug!"); + return; + } + } else { + success = engine.move(move); + if (!success) { + gui.displayMessage("Ungültiger Zug!"); + return; } - return; } - // Normale Züge wie gehabt: - if (engine.move(move)) { - updateGuiBoard(); - gui.updateMoveList(engine.getMoveListStringsGrouped()); - } else { - gui.displayMessage("Ungültiger Zug!"); + updateGuiBoard(); + gui.updateMoveList(engine.getMoveListStringsGrouped()); + + // ---- HIER ist die Matt/Patt/Remis-Prüfung ---- + if (engine.isMated()) { + String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; + gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); + } else if (engine.isStalemate() || engine.isDraw()) { + gui.displayMessage("Remis! (Stalemate oder andere Regel)"); } } + public void updateGuiBoard() { BoardDTO board = engine.getBoardAsDTO(); gui.updateBoard(board); @@ -179,10 +189,13 @@ public class GameController { private void resetFieldBackground(int row, int col) { + Color LIGHT = new Color(0xe0e1dd); + Color DARK = new Color(0x778da9); if ((row + col) % 2 == 0) { - gui.getField(row, col).setBackground(new Color(0x778da9)); + gui.getField(row, col).setBackground(LIGHT); } else { - gui.getField(row, col).setBackground(new Color(0xe0e1dd)); + gui.getField(row, col).setBackground(DARK); } } + } 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 5dfe8ef..5d9dac3 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 @@ -33,6 +33,9 @@ public class GameGui { JButton btnNext = new JButton(">"); JButton btnLast = new JButton(">|"); + Color LIGHT = new Color(0xe0e1dd); + Color DARK = new Color(0x778da9); + private JPanel moveListPanel; private JScrollPane moveListScroll; @@ -54,20 +57,21 @@ public class GameGui { } private void initFields() { - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 8; col++) { - JLabel label = new JLabel("", SwingConstants.CENTER); - label.setOpaque(true); - label.setFont(new Font("Serif", Font.BOLD, 40)); - if ((row + col) % 2 == 0) { - label.setBackground(new Color(0x778da9)); - } else { - label.setBackground(new Color(0xe0e1dd)); - } - fields[row][col] = label; - } - } - } + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JLabel label = new JLabel("", SwingConstants.CENTER); + label.setOpaque(true); + label.setFont(new Font("Serif", Font.BOLD, 40)); + // Richtige Schachfärbung: + if ((row + col) % 2 == 0) { + label.setBackground(LIGHT); // a1 ist jetzt hell! + } else { + label.setBackground(DARK); + } + fields[row][col] = label; + } + } + } public JPanel mainPanel() { JPanel mainPanel = new JPanel(new GridBagLayout()); @@ -97,29 +101,27 @@ public class GameGui { } public JPanel boardPanel() { - JPanel boardPanel = new JPanel(new GridLayout(8, 8)); - boardPanel.setPreferredSize(new Dimension(800, 800)); + JPanel boardPanel = new JPanel(new GridLayout(8, 8)); + boardPanel.setPreferredSize(new Dimension(800, 800)); - for (int row = 0; row < 8; row++) { - - for (int col = 0; col < 8; col++) { - - JLabel label = new JLabel("", SwingConstants.CENTER); - label.setOpaque(true); - label.setFont(new Font("Serif", Font.BOLD, 70)); - if ((row + col) % 2 == 0) { - label.setBackground(new Color(0x778da9)); - } else { - label.setBackground(new Color(0xe0e1dd)); - } - fields[row][col] = label; - boardPanel.add(label); - - } - } - boardPanel.setBackground(new Color(0x1b263b)); - return boardPanel; - } + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JLabel label = new JLabel("", SwingConstants.CENTER); + label.setOpaque(true); + label.setFont(new Font("Serif", Font.BOLD, 70)); + // Richtige Schachfärbung: + if ((row + col) % 2 == 0) { + label.setBackground(LIGHT); + } else { + label.setBackground(DARK); + } + fields[row][col] = label; + boardPanel.add(label); + } + } + boardPanel.setBackground(new Color(0x1b263b)); + return boardPanel; + } public JPanel chessPanel(JPanel panel) { JPanel chessPanel = new JPanel(new GridBagLayout()); From 0c5066fd62c4672be354a426dee268f8cbd1afa1 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:01:06 +0200 Subject: [PATCH 11/32] New CreativeController class --- .../informatik/chess/controller/CreativeController.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java new file mode 100644 index 0000000..a3968d8 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java @@ -0,0 +1,5 @@ +package de.hs_mannheim.informatik.chess.controller; + +public class CreativeController { + +} From 4ee2e021ede9eab3704d9133f72e7e1f33c7873d Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:01:35 +0200 Subject: [PATCH 12/32] New PgnController class --- .../informatik/chess/controller/PgnController.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java new file mode 100644 index 0000000..76d7ad2 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java @@ -0,0 +1,5 @@ +package de.hs_mannheim.informatik.chess.controller; + +public class PgnController { + +} From 5d9f4e2b8e8390f6fe61a300068f173f51c50433 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:34:13 +0200 Subject: [PATCH 13/32] New Method setPositionFromFEN in ChessEngine --- .../de/hs_mannheim/informatik/chess/model/ChessEngine.java | 5 +++++ 1 file changed, 5 insertions(+) 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 4207c6b..8f178df 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 @@ -177,6 +177,11 @@ public class ChessEngine { return new PieceDTO(type, color, symbol); } + public void setPositionFromFEN(String fen) { + board.loadFromFen(fen); + } + + public boolean isMated() { boolean mated = board.isMated(); logger.info("isMated() = " + mated); From a36fb3b69d4a5f84143e26af3f0cddbea553d029 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:35:43 +0200 Subject: [PATCH 14/32] Fully implemented CreativeController class --- .../chess/controller/CreativeController.java | 155 +++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java index a3968d8..85b2a52 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java @@ -1,5 +1,158 @@ package de.hs_mannheim.informatik.chess.controller; -public class CreativeController { +import de.hs_mannheim.informatik.chess.view.CreativeGui; +import de.hs_mannheim.informatik.chess.model.ChessEngine; +import de.hs_mannheim.informatik.chess.model.PieceDTO; +import javax.swing.*; +import java.awt.event.MouseListener; +import java.util.HashMap; +import java.util.Map; + +public class CreativeController { + private CreativeGui gui; + private ChessEngine engine; + + // Hilfsmap für Unicode zu Chesslib Piece FEN-Zeichen + private static final HashMap PIECE_TO_FEN = createPieceToFenMap(); + + private static HashMap createPieceToFenMap() { + HashMap map = new HashMap(); + map.put("BLACK_KING", 'k'); + map.put("BLACK_QUEEN", 'q'); + map.put("BLACK_ROOK", 'r'); + map.put("BLACK_BISHOP", 'b'); + map.put("BLACK_KNIGHT", 'n'); + map.put("BLACK_PAWN", 'p'); + map.put("WHITE_KING", 'K'); + map.put("WHITE_QUEEN", 'Q'); + map.put("WHITE_ROOK", 'R'); + map.put("WHITE_BISHOP", 'B'); + map.put("WHITE_KNIGHT", 'N'); + map.put("WHITE_PAWN", 'P'); + return map; + } + + public CreativeController(CreativeGui gui, ChessEngine engine) { + this.gui = gui; + this.engine = engine; + + setupFieldListeners(); + setupFenUpdateListener(); + gui.setFenText(boardToFen()); + } + + private void setupFieldListeners() { + JLabel[][] fields = gui.getFields(); + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + final int r = row, c = col; + for (MouseListener l : fields[r][c].getMouseListeners()) { + fields[r][c].removeMouseListener(l); + } + fields[r][c].addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + fieldClicked(r, c); + } + }); + } + } + } + + private void fieldClicked(int row, int col) { + // Nach jedem Klick: FEN aktualisieren + SwingUtilities.invokeLater(new Runnable() { + public void run() { + String fen = boardToFen(); + gui.setFenText(fen); + } + }); + } + + private void setupFenUpdateListener() { + //Getter für das Feld in deiner Gui: + JTextField fenField = gui.getFenField(); + if (fenField != null) { + fenField.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + applyFenToBoard(); + } + }); + } + + JButton updateBtn = gui.getUpdateButton(); + if (updateBtn != null) { + updateBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent e) { + applyFenToBoard(); + } + }); + } + } + + private void applyFenToBoard() { + String fen = gui.getFenText(); + try { + engine.setPositionFromFEN(fen); + updateGuiFromEngine(); + } catch (Exception ex) { + JOptionPane.showMessageDialog(null, "Ungültiges FEN: " + ex.getMessage()); + } + } + + // Board aus der GUI zu FEN String + private String boardToFen() { + JLabel[][] fields = gui.getFields(); + StringBuilder fen = new StringBuilder(); + for (int row = 0; row < 8; row++) { + int empty = 0; + for (int col = 0; col < 8; col++) { + String symbol = fields[row][col].getText(); + String pieceKey = null; + for (Map.Entry entry : CreativeGui.UNICODE_MAP.entrySet()) { + if (entry.getValue().equals(symbol)) { + pieceKey = entry.getKey(); + break; + } + } + if (pieceKey == null || "ERASER".equals(pieceKey) || symbol.trim().isEmpty()) { + empty++; + } else { + if (empty > 0) { + fen.append(empty); + empty = 0; + } + Character fenChar = PIECE_TO_FEN.get(pieceKey); + if (fenChar != null) fen.append(fenChar); + } + } + if (empty > 0) fen.append(empty); + if (row < 7) fen.append('/'); + } + fen.append(" w - - 0 1"); + return fen.toString(); + } + + private void updateGuiFromEngine() { + // Lies das BoardDTO und update die Felder + PieceDTO[][] board = engine.getBoardAsDTO().getBoard(); + JLabel[][] fields = gui.getFields(); + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + PieceDTO p = board[row][col]; + if (p == null) { + fields[row][col].setText(""); + } else { + // Suche Symbol-String in UNICODE_MAP + for (Map.Entry entry : CreativeGui.UNICODE_MAP.entrySet()) { + if (entry.getKey().startsWith(p.getColor()) && entry.getKey().endsWith(p.getType())) { + fields[row][col].setText(entry.getValue()); + break; + } + } + } + } + } + } } From 5dcdabb2150ba7bf7655cf5c70ab7dc71fb03143 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:35:57 +0200 Subject: [PATCH 15/32] Fully implemented CreativeGui class --- .../informatik/chess/view/CreativeGui.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java index 56b0582..bd142e9 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java @@ -1,5 +1,150 @@ package de.hs_mannheim.informatik.chess.view; +import javax.swing.*; +import java.awt.*; +import java.util.HashMap; + public class CreativeGui { + private JFrame frame; + private JLabel[][] fields = new JLabel[8][8]; + private String selectedPiece = null; // z.B. "BLACK_KING", "WHITE_QUEEN" + private JTextField fenField; + private JButton flipBoardButton; + private JButton updateBtn; + + // Unicode-Map für die Pieces (schneller Zugriff) + public static final HashMap UNICODE_MAP = new HashMap() {{ + put("BLACK_KING", "\u265A"); put("BLACK_QUEEN", "\u265B"); + put("BLACK_ROOK", "\u265C"); put("BLACK_BISHOP", "\u265D"); + put("BLACK_KNIGHT", "\u265E"); put("BLACK_PAWN", "\u265F"); + put("WHITE_KING", "\u2654"); put("WHITE_QUEEN", "\u2655"); + put("WHITE_ROOK", "\u2656"); put("WHITE_BISHOP", "\u2657"); + put("WHITE_KNIGHT", "\u2658"); put("WHITE_PAWN", "\u2659"); + put("ERASER", "\u2716"); // oder anderes Icon zum Löschen + }}; + + private final Color LIGHT = new Color(0xe0e1dd); + private final Color DARK = new Color(0x778da9); + + public CreativeGui() { + frame = new JFrame("Creative Mode"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setSize(1200, 1000); + frame.setLocationRelativeTo(null); + + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBackground(LIGHT); + + mainPanel.add(toolbarPanel(false), BorderLayout.NORTH); + mainPanel.add(boardPanel(), BorderLayout.CENTER); + mainPanel.add(toolbarPanel(true), BorderLayout.SOUTH); + mainPanel.add(fenPanel(), BorderLayout.EAST); + + frame.setContentPane(mainPanel); + frame.setVisible(true); + } + + // Toolbar: oben = schwarz, unten = weiß + private JPanel toolbarPanel(boolean white) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.setBackground(new Color(0x1b263b)); + + String prefix = white ? "WHITE_" : "BLACK_"; + String[] pieces = {"KING", "QUEEN", "ROOK", "BISHOP", "KNIGHT", "PAWN"}; + + for (String type : pieces) { + JButton btn = new JButton(UNICODE_MAP.get(prefix + type)); + btn.setFont(new Font("Serif", Font.BOLD, 45)); + btn.setFocusPainted(false); + btn.setBackground(white ? LIGHT : DARK); + btn.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + String key = prefix + type; + btn.addActionListener(e -> selectedPiece = key); + panel.add(btn); + } + + // Löschen-Button + JButton del = new JButton(UNICODE_MAP.get("ERASER")); + del.setFont(new Font("Serif", Font.BOLD, 40)); + del.setBackground(Color.PINK); + del.setFocusPainted(false); + del.addActionListener(e -> selectedPiece = "ERASER"); + panel.add(Box.createRigidArea(new Dimension(25, 0))); + panel.add(del); + + return panel; + } + + // Schachbrett + private JPanel boardPanel() { + JPanel boardPanel = new JPanel(new GridLayout(8, 8)); + boardPanel.setPreferredSize(new Dimension(800, 800)); + boolean flip = false; // später per Button änderbar + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JLabel label = new JLabel("", SwingConstants.CENTER); + label.setOpaque(true); + label.setFont(new Font("Serif", Font.BOLD, 70)); + label.setBackground((row + col) % 2 == 0 ? LIGHT : DARK); + + int r = row, c = col; // für Lambda final + label.addMouseListener(new java.awt.event.MouseAdapter() { + public void mousePressed(java.awt.event.MouseEvent evt) { + handleBoardClick(r, c); + } + }); + fields[row][col] = label; + boardPanel.add(label); + } + } + return boardPanel; + } + + // FEN-Panel (rechts) + private JPanel fenPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBackground(new Color(0xe0e1dd)); + panel.setPreferredSize(new Dimension(250, 150)); + + panel.add(new JLabel("FEN:")); + fenField = new JTextField(""); + fenField.setFont(new Font("Monospaced", Font.PLAIN, 15)); + panel.add(fenField); + + updateBtn = new JButton("Update Board"); // Nicht mehr als lokale Variable! + updateBtn.addActionListener(e -> {/* TODO: set board from fenField.getText()! */}); + panel.add(Box.createRigidArea(new Dimension(0, 10))); + panel.add(updateBtn); + + return panel; + } + + private void handleBoardClick(int row, int col) { + if (selectedPiece == null) return; + if ("ERASER".equals(selectedPiece)) { + fields[row][col].setText(""); + } else { + fields[row][col].setText(UNICODE_MAP.get(selectedPiece)); + } + // TODO: Board-State/FEN updaten + } + + // Getter für Controller etc. + public JLabel[][] getFields() { return fields; } + public String getFenText() { return fenField.getText(); } + public void setFenText(String fen) { fenField.setText(fen); } + + // CreativeGui.java + public JTextField getFenField() { + return fenField; + } + public JButton getUpdateButton() { + // Musst du im Konstruktor speichern (z.B. als Attribut), falls noch nicht! + return updateBtn; + } } + From 6b4cbda25041b70dc7d016f9b2af10585b113c92 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 01:37:09 +0200 Subject: [PATCH 16/32] MainController now has a fully implemented CreativeGui --- .../informatik/chess/controller/MainController.java | 4 ++++ 1 file changed, 4 insertions(+) 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 01e0212..45661ed 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 @@ -1,6 +1,7 @@ package de.hs_mannheim.informatik.chess.controller; import de.hs_mannheim.informatik.chess.view.MainGui; +import de.hs_mannheim.informatik.chess.view.CreativeGui; import de.hs_mannheim.informatik.chess.view.GameGui; import de.hs_mannheim.informatik.chess.model.ChessEngine; @@ -24,6 +25,9 @@ public class MainController { private void startCreativeMode() { mainGui.close(); + CreativeGui creativegui = new CreativeGui(); + ChessEngine engine = new ChessEngine(); + new CreativeController(creativegui, engine); // Hier z.B. CreativeSetupGui öffnen (siehe oben) } From f80fb99ba12abd86f4a71e081fe2d988fb6c53e3 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 02:28:59 +0200 Subject: [PATCH 17/32] Reworked whole creativeMode --- .../chess/controller/CreativeController.java | 35 +++-- .../informatik/chess/view/CreativeGui.java | 147 +++++++++++------- 2 files changed, 110 insertions(+), 72 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java index 85b2a52..9d961e1 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/CreativeController.java @@ -39,7 +39,8 @@ public class CreativeController { setupFieldListeners(); setupFenUpdateListener(); - gui.setFenText(boardToFen()); + gui.setFenText("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); + applyFenToBoard(); } private void setupFieldListeners() { @@ -57,18 +58,26 @@ public class CreativeController { }); } } + gui.getFlipBoardButton().addActionListener(e -> { + applyFenToBoard(); + gui.setFlipped(!gui.isFlipped()); + updateGuiFromEngine(); // Board wird neu angezeigt, ggf. invertiert + }); } private void fieldClicked(int row, int col) { - // Nach jedem Klick: FEN aktualisieren - SwingUtilities.invokeLater(new Runnable() { - public void run() { - String fen = boardToFen(); - gui.setFenText(fen); - } - }); + String selectedPiece = gui.getSelectedPiece(); + if (selectedPiece == null) return; + if ("ERASER".equals(selectedPiece)) { + gui.getFields()[row][col].setText(""); + } else { + gui.getFields()[row][col].setText(CreativeGui.UNICODE_MAP.get(selectedPiece)); + } + // Jetzt FEN synchronisieren: + gui.setFenText(boardToFen()); } + private void setupFenUpdateListener() { //Getter für das Feld in deiner Gui: JTextField fenField = gui.getFenField(); @@ -134,20 +143,22 @@ public class CreativeController { } private void updateGuiFromEngine() { - // Lies das BoardDTO und update die Felder PieceDTO[][] board = engine.getBoardAsDTO().getBoard(); JLabel[][] fields = gui.getFields(); + boolean flipped = gui.isFlipped(); // NEU + for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { + int displayRow = flipped ? 7 - row : row; + int displayCol = flipped ? 7 - col : col; PieceDTO p = board[row][col]; if (p == null) { - fields[row][col].setText(""); + fields[displayRow][displayCol].setText(""); } else { - // Suche Symbol-String in UNICODE_MAP for (Map.Entry entry : CreativeGui.UNICODE_MAP.entrySet()) { if (entry.getKey().startsWith(p.getColor()) && entry.getKey().endsWith(p.getType())) { - fields[row][col].setText(entry.getValue()); + fields[displayRow][displayCol].setText(entry.getValue()); break; } } diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java index bd142e9..4f146b9 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/CreativeGui.java @@ -5,14 +5,15 @@ import java.awt.*; import java.util.HashMap; public class CreativeGui { + private boolean isFlipped = false; + private JFrame frame; private JLabel[][] fields = new JLabel[8][8]; - private String selectedPiece = null; // z.B. "BLACK_KING", "WHITE_QUEEN" + private String selectedPiece = null; private JTextField fenField; - private JButton flipBoardButton; private JButton updateBtn; + private JButton flipBoardButton; - // Unicode-Map für die Pieces (schneller Zugriff) public static final HashMap UNICODE_MAP = new HashMap() {{ put("BLACK_KING", "\u265A"); put("BLACK_QUEEN", "\u265B"); put("BLACK_ROOK", "\u265C"); put("BLACK_BISHOP", "\u265D"); @@ -20,7 +21,7 @@ public class CreativeGui { put("WHITE_KING", "\u2654"); put("WHITE_QUEEN", "\u2655"); put("WHITE_ROOK", "\u2656"); put("WHITE_BISHOP", "\u2657"); put("WHITE_KNIGHT", "\u2658"); put("WHITE_PAWN", "\u2659"); - put("ERASER", "\u2716"); // oder anderes Icon zum Löschen + put("ERASER", "\u2716"); }}; private final Color LIGHT = new Color(0xe0e1dd); @@ -29,122 +30,148 @@ public class CreativeGui { public CreativeGui() { frame = new JFrame("Creative Mode"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.setSize(1200, 1000); + frame.setSize(1600, 1200); frame.setLocationRelativeTo(null); - JPanel mainPanel = new JPanel(new BorderLayout()); + JPanel mainPanel = new JPanel(new GridBagLayout()); mainPanel.setBackground(LIGHT); + GridBagConstraints gbc = new GridBagConstraints(); - mainPanel.add(toolbarPanel(false), BorderLayout.NORTH); - mainPanel.add(boardPanel(), BorderLayout.CENTER); - mainPanel.add(toolbarPanel(true), BorderLayout.SOUTH); - mainPanel.add(fenPanel(), BorderLayout.EAST); + // LINKS: chessPanel (Board+Toolbars) + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.6; + gbc.weighty = 1.0; + gbc.insets = new Insets(5, 5, 5, 0); + gbc.fill = GridBagConstraints.BOTH; + mainPanel.add(chessPanel(), gbc); + + // RECHTS: FEN & Optionen + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 0.4; + gbc.weighty = 1.0; + gbc.insets = new Insets(5, 0, 5, 5); + gbc.fill = GridBagConstraints.BOTH; + mainPanel.add(fenPanel(), gbc); frame.setContentPane(mainPanel); frame.setVisible(true); } - // Toolbar: oben = schwarz, unten = weiß private JPanel toolbarPanel(boolean white) { JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); panel.setBackground(new Color(0x1b263b)); - String prefix = white ? "WHITE_" : "BLACK_"; String[] pieces = {"KING", "QUEEN", "ROOK", "BISHOP", "KNIGHT", "PAWN"}; - for (String type : pieces) { JButton btn = new JButton(UNICODE_MAP.get(prefix + type)); - btn.setFont(new Font("Serif", Font.BOLD, 45)); + btn.setFont(new Font("Serif", Font.BOLD, 40)); btn.setFocusPainted(false); + btn.setPreferredSize(new Dimension(70, 70)); btn.setBackground(white ? LIGHT : DARK); - btn.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + btn.setBorder(BorderFactory.createEmptyBorder(20, 0, 20, 0)); String key = prefix + type; btn.addActionListener(e -> selectedPiece = key); panel.add(btn); } - - // Löschen-Button JButton del = new JButton(UNICODE_MAP.get("ERASER")); - del.setFont(new Font("Serif", Font.BOLD, 40)); + del.setFont(new Font("Serif", Font.BOLD, 36)); del.setBackground(Color.PINK); + del.setPreferredSize(new Dimension(70, 70)); del.setFocusPainted(false); del.addActionListener(e -> selectedPiece = "ERASER"); - panel.add(Box.createRigidArea(new Dimension(25, 0))); panel.add(del); return panel; } - // Schachbrett private JPanel boardPanel() { JPanel boardPanel = new JPanel(new GridLayout(8, 8)); boardPanel.setPreferredSize(new Dimension(800, 800)); - boolean flip = false; // später per Button änderbar - for (int row = 0; row < 8; row++) { for (int col = 0; col < 8; col++) { JLabel label = new JLabel("", SwingConstants.CENTER); label.setOpaque(true); label.setFont(new Font("Serif", Font.BOLD, 70)); label.setBackground((row + col) % 2 == 0 ? LIGHT : DARK); - - int r = row, c = col; // für Lambda final - label.addMouseListener(new java.awt.event.MouseAdapter() { - public void mousePressed(java.awt.event.MouseEvent evt) { - handleBoardClick(r, c); - } - }); fields[row][col] = label; boardPanel.add(label); } } return boardPanel; } + + private JPanel chessPanel() { + JPanel chessPanel = new JPanel(new GridBagLayout()); + chessPanel.setBackground(new Color(0x1b263b)); + GridBagConstraints gbc = new GridBagConstraints(); - // FEN-Panel (rechts) + // Toolbar oben + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + chessPanel.add(toolbarPanel(false), gbc); + + // Board + gbc.gridx = 0; + gbc.gridy = 1; + gbc.fill = GridBagConstraints.BOTH; + chessPanel.add(boardPanel(), gbc); + + // Toolbar unten + gbc.gridx = 0; + gbc.gridy = 2; + gbc.fill = GridBagConstraints.HORIZONTAL; + chessPanel.add(toolbarPanel(true), gbc); + + // Drehknopf + flipBoardButton = new JButton("⇵"); + flipBoardButton.setPreferredSize(new Dimension(70, 70)); + flipBoardButton.setFont(new Font("SansSerif", Font.BOLD, 40)); + flipBoardButton.setBackground(new Color(0x5500ff)); + flipBoardButton.setForeground(Color.WHITE); + flipBoardButton.setFocusPainted(false); + + GridBagConstraints btn = new GridBagConstraints(); + btn.gridx = 0; + btn.gridy = 2; + btn.weightx = 0.0; + btn.weighty = 0.0; + btn.anchor = GridBagConstraints.EAST; + btn.insets = new Insets(5, 0, 0, 0); + chessPanel.add(flipBoardButton, btn); + + return chessPanel; + } + private JPanel fenPanel() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setBackground(new Color(0xe0e1dd)); - panel.setPreferredSize(new Dimension(250, 150)); - + panel.setPreferredSize(new Dimension(420, 40)); panel.add(new JLabel("FEN:")); fenField = new JTextField(""); - fenField.setFont(new Font("Monospaced", Font.PLAIN, 15)); + fenField.setFont(new Font("Monospaced", Font.PLAIN, 22)); + fenField.setMaximumSize(new Dimension(600, 50)); panel.add(fenField); - - updateBtn = new JButton("Update Board"); // Nicht mehr als lokale Variable! - updateBtn.addActionListener(e -> {/* TODO: set board from fenField.getText()! */}); - panel.add(Box.createRigidArea(new Dimension(0, 10))); + updateBtn = new JButton("Update Board"); + updateBtn.setAlignmentX(Component.CENTER_ALIGNMENT); panel.add(updateBtn); - return panel; } - private void handleBoardClick(int row, int col) { - if (selectedPiece == null) return; - if ("ERASER".equals(selectedPiece)) { - fields[row][col].setText(""); - } else { - fields[row][col].setText(UNICODE_MAP.get(selectedPiece)); - } - // TODO: Board-State/FEN updaten - } - - // Getter für Controller etc. + // Für Controller public JLabel[][] getFields() { return fields; } public String getFenText() { return fenField.getText(); } public void setFenText(String fen) { fenField.setText(fen); } + public JTextField getFenField() { return fenField; } + public JButton getUpdateButton() { return updateBtn; } + public void setSelectedPiece(String piece) { selectedPiece = piece; } + public String getSelectedPiece() { return selectedPiece; } - // CreativeGui.java - public JTextField getFenField() { - return fenField; - } - public JButton getUpdateButton() { - // Musst du im Konstruktor speichern (z.B. als Attribut), falls noch nicht! - return updateBtn; - } - + public boolean isFlipped() { return isFlipped; } + public void setFlipped(boolean f) { isFlipped = f; } + public JButton getFlipBoardButton() { return flipBoardButton; } } - From 6273261fd0ef56d956e8865f836de47991aa8475 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 02:40:30 +0200 Subject: [PATCH 18/32] Added methods to PgnGui class --- .../informatik/chess/view/PgnGui.java | 270 +++++++++++++++++- 1 file changed, 269 insertions(+), 1 deletion(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java index 459b893..b01b9c6 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnGui.java @@ -1,5 +1,273 @@ package de.hs_mannheim.informatik.chess.view; -public class PgnGui { +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.util.List; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingConstants; + +import de.hs_mannheim.informatik.chess.model.BoardDTO; +import de.hs_mannheim.informatik.chess.model.PieceDTO; + +public class PgnGui { + + private JLabel[][] fields = new JLabel[8][8]; + private JButton flipBoardButton; + private boolean isFlipped = false; + + JButton btnFirst = new JButton("|<"); + JButton btnPrev = new JButton("<"); + JButton btnNext = new JButton(">"); + JButton btnLast = new JButton(">|"); + + Color LIGHT = new Color(0xe0e1dd); + Color DARK = new Color(0x778da9); + + private JPanel moveListPanel; + private JScrollPane moveListScroll; + + public PgnGui(){ + initFields(); + mainFrame(); + } + + + public JFrame mainFrame() { + JFrame frame = new JFrame(); + frame.setSize(1600, 1000); + frame.setLocationRelativeTo(null); + frame.add(mainPanel()); + + frame.setDefaultCloseOperation(2); + frame.setVisible(true); + return frame; + } + + private void initFields() { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JLabel label = new JLabel("", SwingConstants.CENTER); + label.setOpaque(true); + label.setFont(new Font("Serif", Font.BOLD, 40)); + // Richtige Schachfärbung: + if ((row + col) % 2 == 0) { + label.setBackground(LIGHT); // a1 ist jetzt hell! + } else { + label.setBackground(DARK); + } + fields[row][col] = label; + } + } + } + + public JPanel mainPanel() { + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + mainPanel.setBackground(new Color(0xe0e1dd)); + // Links (Schach) + gbc.gridx = 0; + gbc.gridy = 0; + gbc.weightx = 0.6; + gbc.weighty = 1.0; + gbc.insets = new Insets(5, 5, 5, 0); + //oben, links, unten, rechts + gbc.fill = GridBagConstraints.BOTH; + mainPanel.add(chessPanel(boardPanel()), gbc); + + // Rechts (Stats) + gbc.gridx = 1; + gbc.gridy = 0; + gbc.weightx = 0.4; + gbc.weighty = 1.0; + gbc.insets = new Insets(5, 0, 5, 5); + //oben, links, unten, rechts + gbc.fill = GridBagConstraints.BOTH; + mainPanel.add(statsPanel(), gbc); + + return mainPanel; + } + + public JPanel boardPanel() { + JPanel boardPanel = new JPanel(new GridLayout(8, 8)); + boardPanel.setPreferredSize(new Dimension(800, 800)); + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + JLabel label = new JLabel("", SwingConstants.CENTER); + label.setOpaque(true); + label.setFont(new Font("Serif", Font.BOLD, 70)); + // Richtige Schachfärbung: + if ((row + col) % 2 == 0) { + label.setBackground(LIGHT); + } else { + label.setBackground(DARK); + } + fields[row][col] = label; + boardPanel.add(label); + } + } + boardPanel.setBackground(new Color(0x1b263b)); + return boardPanel; + } + + public JPanel chessPanel(JPanel panel) { + JPanel chessPanel = new JPanel(new GridBagLayout()); + GridBagConstraints board = new GridBagConstraints(); + chessPanel.setBackground(new Color(0x1b263b)); + board.gridx = 0; + board.gridy = 0; + board.weightx = 0.7; + board.weighty = 1.0; + board.insets = new Insets(0, 0, 0, 0); + //oben, links, unten, rechts + board.fill = GridBagConstraints.BOTH; + chessPanel.add(panel); + // Button unten rechts + flipBoardButton = new JButton("⇵"); + flipBoardButton.setPreferredSize(new Dimension(70, 70)); + flipBoardButton.setFont(new Font("SansSerif", Font.BOLD, 40)); + flipBoardButton.setBackground(new Color(0x5500ff)); + flipBoardButton.setForeground(Color.WHITE); + flipBoardButton.setFocusPainted(false); + + GridBagConstraints btn = new GridBagConstraints(); + btn.gridx = 0; + btn.gridy = 1; + btn.weightx = 0.0; + btn.weighty = 0.0; + btn.anchor = GridBagConstraints.SOUTHEAST; + btn.insets = new Insets(10, 0, 0, 0); + + chessPanel.add(flipBoardButton, btn); + + return chessPanel; + } + + public JPanel statsPanel() { + JPanel statsPanel = new JPanel(new BorderLayout()); + statsPanel.setBackground(new Color(0x0d1b2a)); + + // Move-Liste + moveListPanel = new JPanel(); + moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS)); + moveListPanel.setBackground(new Color(0x0d1b2a)); + moveListScroll = new JScrollPane(moveListPanel); + moveListScroll.setPreferredSize(new Dimension(250, 800)); + statsPanel.add(moveListScroll, BorderLayout.CENTER); + + // Button-Leiste + JPanel buttonPanel = new JPanel(); + buttonPanel.setBackground(new Color(0x0d1b2a)); + // Grid oder Flow + buttonPanel.setLayout(new GridLayout(1, 4, 10, 0)); + + // Style (optional) + 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); + + // Hinzufügen + buttonPanel.add(btnFirst); + buttonPanel.add(btnPrev); + buttonPanel.add(btnNext); + buttonPanel.add(btnLast); + + // Unten ins BorderLayout + statsPanel.add(buttonPanel, BorderLayout.SOUTH); + + return statsPanel; + } + + public String showPromotionDialog(String color) { + String[] choices = {"Dame", "Turm", "Springer", "Läufer"}; + String choice = (String) JOptionPane.showInputDialog( + null, + color + "er Bauer wird umgewandelt – wähle Figur:", + "Bauernumwandlung", + JOptionPane.PLAIN_MESSAGE, + null, + choices, + choices[0]); + if (choice == null) return "QUEEN"; + switch (choice) { + case "Turm": return "ROOK"; + case "Springer": return "KNIGHT"; + case "Läufer": return "BISHOP"; + default: return "QUEEN"; + } + } + + public void updateMoveList(List moves) { + moveListPanel.removeAll(); + for (String move : moves) { + JLabel moveLabel = new JLabel(move); + moveLabel.setForeground(Color.WHITE); + moveLabel.setFont(new Font("SansSerif", Font.PLAIN, 18)); + moveListPanel.add(moveLabel); + } + moveListPanel.revalidate(); + moveListPanel.repaint(); + } + + public JLabel getField(int row, int col) { + return fields[row][col]; + } + + public JButton getFlipBoardButton() { + return flipBoardButton; + } + + public boolean isFlipped() { + return isFlipped; + } + + public void setFlipped(boolean flipped) { + this.isFlipped = flipped; + } + + public JButton getBtnFirst() { return btnFirst; } + public JButton getBtnPrev() { return btnPrev; } + public JButton getBtnNext() { return btnNext; } + public JButton getBtnLast() { return btnLast; } + + public void updateBoard(BoardDTO boardDTO) { + PieceDTO[][] board = boardDTO.getBoard(); + if (!isFlipped) { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + PieceDTO piece = board[row][col]; + fields[row][col].setText(piece != null ? piece.getUnicodeSymbol() : ""); + } + } + } else { + // Invertiere Zeilen und Spalten + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + PieceDTO piece = board[7 - row][7 - col]; + fields[row][col].setText(piece != null ? piece.getUnicodeSymbol() : ""); + } + } + } + } + + + public void displayMessage(String msg) { + JOptionPane.showMessageDialog(null, msg); + } + } + From 778db90bc5410530ff4f0e587c7d576386ce38d6 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 02:43:46 +0200 Subject: [PATCH 19/32] Added all methods to PgnController --- .../chess/controller/PgnController.java | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java index 76d7ad2..83baabd 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java @@ -1,5 +1,202 @@ package de.hs_mannheim.informatik.chess.controller; +import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.BorderFactory; + +import de.hs_mannheim.informatik.chess.model.ChessEngine; +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.PgnGui; + public class PgnController { + PgnGui gui; + ChessEngine engine; + private int selectedRow = -1, selectedCol = -1; + private List highlightedFields = new ArrayList<>(); + + public PgnController(PgnGui pgngui, ChessEngine engine) { + this.gui = pgngui; + this.engine = engine; + initListeners(); + updateGuiBoard(); + } + + private int flipRow(int row) { + return gui.isFlipped() ? 7 - row : row; + } + private int flipCol(int col) { + return gui.isFlipped() ? 7 - col : col; + } + + private void initListeners() { + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + final int r = row, c = col; + gui.getField(row, col).addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + handleClick(r, c); + } + }); + } + } + + // Erster Zug + gui.getBtnFirst().addActionListener(e -> { + engine.setPositionToMoveIndex(0); + updateGuiBoard(); + }); + // Ein Zug zurück + gui.getBtnPrev().addActionListener(e -> { + int idx = Math.max(0, engine.getCurrentMoveIndex() - 1); + engine.setPositionToMoveIndex(idx); + updateGuiBoard(); + }); + // Ein Zug vor + gui.getBtnNext().addActionListener(e -> { + int idx = Math.min(engine.getMoveListSize(), engine.getCurrentMoveIndex() + 1); + engine.setPositionToMoveIndex(idx); + updateGuiBoard(); + }); + // Letzter Zug + gui.getBtnLast().addActionListener(e -> { + engine.setPositionToMoveIndex(engine.getMoveListSize()); + updateGuiBoard(); + }); + + + gui.getFlipBoardButton().addActionListener(e -> { + //ALLE Highlights und Borders zurücksetzen + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + resetFieldBackground(row, col); + gui.getField(row, col).setBorder(null); + } + } + highlightedFields.clear(); + selectedRow = -1; + selectedCol = -1; + + // 2. Flip-Zustand ändern + gui.setFlipped(!gui.isFlipped()); + + // 3. Board neu zeichnen + updateGuiBoard(); + }); + + + } + + private void handleClick(int guiRow, int guiCol) { + int modelRow = flipRow(guiRow); + int modelCol = flipCol(guiCol); + + //Figur am Feld + BoardDTO boardDTO = engine.getBoardAsDTO(); + PieceDTO piece = boardDTO.getBoard()[modelRow][modelCol]; + + //Ist eine Figur da und hat sie die aktuelle Farbe + String amZug = engine.getCurrentPlayer(); // "WHITE" oder "BLACK" + if (selectedRow == -1 && selectedCol == -1) { + if (piece == null || !piece.getColor().equals(amZug)) { + // Falsche Farbe oder leeres Feld -> abbrechen, keine Highlights! + return; + } + + selectedRow = modelRow; + selectedCol = modelCol; + gui.getField(guiRow, guiCol).setBorder(BorderFactory.createLineBorder(new Color(0x1b263b), 7)); + + String fromSquare = coordToChessNotation(modelRow, modelCol); + List moves = engine.getLegalDestinations(fromSquare); + + for (MoveDTO move : moves) { + int guiToRow = gui.isFlipped() ? 7 - move.getToRow() : move.getToRow(); + int guiToCol = gui.isFlipped() ? 7 - move.getToCol() : move.getToCol(); + gui.getField(guiToRow, guiToCol).setBackground(new Color( 27, 38, 59 )); + highlightedFields.add(new int[]{guiToRow, guiToCol}); + } + } else { + for (int[] xy : highlightedFields) { + resetFieldBackground(xy[0], xy[1]); + } + highlightedFields.clear(); + int oldGuiRow = gui.isFlipped() ? 7 - selectedRow : selectedRow; + int oldGuiCol = gui.isFlipped() ? 7 - selectedCol : selectedCol; + gui.getField(oldGuiRow, oldGuiCol).setBorder(null); + + MoveDTO move = new MoveDTO(selectedRow, selectedCol, modelRow, modelCol); + handleMove(move); + selectedRow = -1; + selectedCol = -1; + } + } + + private void handleMove(MoveDTO move) { + BoardDTO boardDTO = engine.getBoardAsDTO(); + PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()]; + boolean isPawn = piece != null && piece.getType().equals("PAWN"); + boolean isWhitePromotion = isPawn && piece.getColor().equals("WHITE") && move.getToRow() == 0; + boolean isBlackPromotion = isPawn && piece.getColor().equals("BLACK") && move.getToRow() == 7; + + boolean success = false; + + if (isWhitePromotion || isBlackPromotion) { + String color = piece.getColor().equals("WHITE") ? "Weiß" : "Schwarz"; + String promotion = gui.showPromotionDialog(color); + success = engine.moveWithPromotion(move, promotion); + if (!success) { + gui.displayMessage("Ungültiger Promotionszug!"); + return; + } + } else { + success = engine.move(move); + if (!success) { + gui.displayMessage("Ungültiger Zug!"); + return; + } + } + + updateGuiBoard(); + gui.updateMoveList(engine.getMoveListStringsGrouped()); + + // ---- HIER ist die Matt/Patt/Remis-Prüfung ---- + if (engine.isMated()) { + String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; + gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); + } else if (engine.isStalemate() || engine.isDraw()) { + gui.displayMessage("Remis! (Stalemate oder andere Regel)"); + } + } + + + public void updateGuiBoard() { + BoardDTO board = engine.getBoardAsDTO(); + gui.updateBoard(board); + } + + // 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; + } + + + private void resetFieldBackground(int row, int col) { + Color LIGHT = new Color(0xe0e1dd); + Color DARK = new Color(0x778da9); + if ((row + col) % 2 == 0) { + gui.getField(row, col).setBackground(LIGHT); + } else { + gui.getField(row, col).setBackground(DARK); + } + } } + From 0f5e656614f99be2b64836cd892c600688bb7f61 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 02:45:34 +0200 Subject: [PATCH 20/32] Added PgnGui to MainController --- .../informatik/chess/controller/MainController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 45661ed..e6e9970 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 @@ -1,6 +1,7 @@ package de.hs_mannheim.informatik.chess.controller; import de.hs_mannheim.informatik.chess.view.MainGui; +import de.hs_mannheim.informatik.chess.view.PgnGui; import de.hs_mannheim.informatik.chess.view.CreativeGui; import de.hs_mannheim.informatik.chess.view.GameGui; import de.hs_mannheim.informatik.chess.model.ChessEngine; @@ -28,11 +29,12 @@ public class MainController { CreativeGui creativegui = new CreativeGui(); ChessEngine engine = new ChessEngine(); new CreativeController(creativegui, engine); - // Hier z.B. CreativeSetupGui öffnen (siehe oben) } private void startLoadGameMode() { mainGui.close(); - // Hier LoadGame-Logik + PgnGui pgngui = new PgnGui(); + ChessEngine engine = new ChessEngine(); + new PgnController(pgngui, engine); } } \ No newline at end of file From 0d37de1cd923407974fb612e94e21d2553ca8ba6 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 02:57:05 +0200 Subject: [PATCH 21/32] Removed unused methods in PgnController --- .../chess/controller/PgnController.java | 125 +----------------- 1 file changed, 2 insertions(+), 123 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java index 83baabd..9db54b7 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java @@ -1,24 +1,15 @@ package de.hs_mannheim.informatik.chess.controller; import java.awt.Color; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.BorderFactory; import de.hs_mannheim.informatik.chess.model.ChessEngine; -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.PgnGui; public class PgnController { PgnGui gui; ChessEngine engine; - private int selectedRow = -1, selectedCol = -1; - private List highlightedFields = new ArrayList<>(); + public PgnController(PgnGui pgngui, ChessEngine engine) { this.gui = pgngui; @@ -27,25 +18,7 @@ public class PgnController { updateGuiBoard(); } - private int flipRow(int row) { - return gui.isFlipped() ? 7 - row : row; - } - private int flipCol(int col) { - return gui.isFlipped() ? 7 - col : col; - } - - private void initListeners() { - for (int row = 0; row < 8; row++) { - for (int col = 0; col < 8; col++) { - final int r = row, c = col; - gui.getField(row, col).addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - handleClick(r, c); - } - }); - } - } - + private void initListeners() { // Erster Zug gui.getBtnFirst().addActionListener(e -> { engine.setPositionToMoveIndex(0); @@ -78,9 +51,6 @@ public class PgnController { gui.getField(row, col).setBorder(null); } } - highlightedFields.clear(); - selectedRow = -1; - selectedCol = -1; // 2. Flip-Zustand ändern gui.setFlipped(!gui.isFlipped()); @@ -91,103 +61,12 @@ public class PgnController { } - - private void handleClick(int guiRow, int guiCol) { - int modelRow = flipRow(guiRow); - int modelCol = flipCol(guiCol); - - //Figur am Feld - BoardDTO boardDTO = engine.getBoardAsDTO(); - PieceDTO piece = boardDTO.getBoard()[modelRow][modelCol]; - - //Ist eine Figur da und hat sie die aktuelle Farbe - String amZug = engine.getCurrentPlayer(); // "WHITE" oder "BLACK" - if (selectedRow == -1 && selectedCol == -1) { - if (piece == null || !piece.getColor().equals(amZug)) { - // Falsche Farbe oder leeres Feld -> abbrechen, keine Highlights! - return; - } - - selectedRow = modelRow; - selectedCol = modelCol; - gui.getField(guiRow, guiCol).setBorder(BorderFactory.createLineBorder(new Color(0x1b263b), 7)); - - String fromSquare = coordToChessNotation(modelRow, modelCol); - List moves = engine.getLegalDestinations(fromSquare); - - for (MoveDTO move : moves) { - int guiToRow = gui.isFlipped() ? 7 - move.getToRow() : move.getToRow(); - int guiToCol = gui.isFlipped() ? 7 - move.getToCol() : move.getToCol(); - gui.getField(guiToRow, guiToCol).setBackground(new Color( 27, 38, 59 )); - highlightedFields.add(new int[]{guiToRow, guiToCol}); - } - } else { - for (int[] xy : highlightedFields) { - resetFieldBackground(xy[0], xy[1]); - } - highlightedFields.clear(); - int oldGuiRow = gui.isFlipped() ? 7 - selectedRow : selectedRow; - int oldGuiCol = gui.isFlipped() ? 7 - selectedCol : selectedCol; - gui.getField(oldGuiRow, oldGuiCol).setBorder(null); - - MoveDTO move = new MoveDTO(selectedRow, selectedCol, modelRow, modelCol); - handleMove(move); - selectedRow = -1; - selectedCol = -1; - } - } - - private void handleMove(MoveDTO move) { - BoardDTO boardDTO = engine.getBoardAsDTO(); - PieceDTO piece = boardDTO.getBoard()[move.getFromRow()][move.getFromCol()]; - boolean isPawn = piece != null && piece.getType().equals("PAWN"); - boolean isWhitePromotion = isPawn && piece.getColor().equals("WHITE") && move.getToRow() == 0; - boolean isBlackPromotion = isPawn && piece.getColor().equals("BLACK") && move.getToRow() == 7; - - boolean success = false; - - if (isWhitePromotion || isBlackPromotion) { - String color = piece.getColor().equals("WHITE") ? "Weiß" : "Schwarz"; - String promotion = gui.showPromotionDialog(color); - success = engine.moveWithPromotion(move, promotion); - if (!success) { - gui.displayMessage("Ungültiger Promotionszug!"); - return; - } - } else { - success = engine.move(move); - if (!success) { - gui.displayMessage("Ungültiger Zug!"); - return; - } - } - - updateGuiBoard(); - gui.updateMoveList(engine.getMoveListStringsGrouped()); - - // ---- HIER ist die Matt/Patt/Remis-Prüfung ---- - if (engine.isMated()) { - String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß"; - gui.displayMessage(winner + " hat gewonnen (Schachmatt)!"); - } else if (engine.isStalemate() || engine.isDraw()) { - gui.displayMessage("Remis! (Stalemate oder andere Regel)"); - } - } - public void updateGuiBoard() { BoardDTO board = engine.getBoardAsDTO(); gui.updateBoard(board); } - // 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; - } - - private void resetFieldBackground(int row, int col) { Color LIGHT = new Color(0xe0e1dd); Color DARK = new Color(0x778da9); From 24f6339b0635889ea6a1df5a1777c7715eb05c23 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:01:16 +0200 Subject: [PATCH 22/32] Valentins pgn methods for saving and loading pgn files in ChessEngine --- .../informatik/chess/model/ChessEngine.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) 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 8f178df..81288de 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 @@ -1,4 +1,5 @@ package de.hs_mannheim.informatik.chess.model; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.logging.ConsoleHandler; @@ -10,7 +11,9 @@ import java.util.logging.SimpleFormatter; import com.github.bhlangonijr.chesslib.Board; import com.github.bhlangonijr.chesslib.Piece; import com.github.bhlangonijr.chesslib.Square; +import com.github.bhlangonijr.chesslib.game.Game; import com.github.bhlangonijr.chesslib.move.Move; +import com.github.bhlangonijr.chesslib.pgn.PgnHolder; public class ChessEngine { private Board board; @@ -223,4 +226,69 @@ public class ChessEngine { logger.info("ChessEngine wurde initialisiert."); } + public List loadGamesFromPgn(String path) throws IOException { + + PgnHolder pgnHolder = new PgnHolder(path); + try { + pgnHolder.loadPgn(); + } catch (Exception e) { + e.printStackTrace(); + } + List games = pgnHolder.getGames(); + return games; + } + + public void saveAsPgn(Game game, String path, String dateiname) { + String event = game.getRound().getEvent().getName(); + String site = game.getRound().getEvent().getSite(); + String round = "" + game.getRound().getNumber(); + String date = game.getRound().getEvent().getStartDate(); + String wName = game.getWhitePlayer().getName(); + String bName = game.getBlackPlayer().getName(); + String result = game.getResult().getDescription(); + + StringBuilder header = new StringBuilder(); + header.append("[Event \"" + event + "\"]\n"); + header.append("[Site \"" + site + "\"]\n"); + header.append("[Date \"" + date + "\"]\n"); + header.append("[Round \"" + round + "\"]\n"); + header.append("[White \"" + wName + "\"]\n"); + header.append("[Black \"" + bName + "\"]\n"); + header.append("[Result \"" + result + "\"]\n"); + header.append("\n"); + + StringBuilder sb = new StringBuilder(); + String[] sanArray = game.getHalfMoves().toSanArray(); + + for (int i = 0; i < sanArray.length; i++) { + if (i % 2 == 0) { + sb.append((i / 2 + 1)).append(". "); + } + sb.append(sanArray[i]).append(" "); + } + + sb.append(result); // Endergebnis muss auch am Ende stehen! + + String file = header.toString() + sb.toString(); + +// try { +// Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + + public void loadMoves(List moveList) { + board = new Board(); // Neues leeres Brett + moves.clear(); + currentMoveIndex = 0; + + for (Move move : moveList) { + board.doMove(move); + moves.add(move); + currentMoveIndex++; + } + + } + } From f3079dc82e00426bd5235d621914c230b8e66971 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:03:49 +0200 Subject: [PATCH 23/32] Implemented missing librarys --- .../informatik/chess/model/ChessEngine.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) 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 81288de..1b9fb78 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 @@ -1,5 +1,8 @@ package de.hs_mannheim.informatik.chess.model; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.logging.ConsoleHandler; @@ -271,11 +274,11 @@ public class ChessEngine { String file = header.toString() + sb.toString(); -// try { -// Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8); -// } catch (IOException e) { -// e.printStackTrace(); -// } + try { + Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + } } public void loadMoves(List moveList) { From 2e6b9ec81c26b8c033789af11bb79939bdbbec9f Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:38:02 +0200 Subject: [PATCH 24/32] New implemented class PgnSelectionGui --- .../chess/view/PgnSelectionGui.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnSelectionGui.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnSelectionGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnSelectionGui.java new file mode 100644 index 0000000..8bc50fe --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/PgnSelectionGui.java @@ -0,0 +1,39 @@ +package de.hs_mannheim.informatik.chess.view; + +import java.util.List; +import java.util.function.Consumer; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import com.github.bhlangonijr.chesslib.game.Game; + +public class PgnSelectionGui extends JFrame { + public PgnSelectionGui(List games, Consumer onGameSelected) { + setTitle("Geladene Partien"); + setSize(600, 800); + setLocationRelativeTo(null); + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + JScrollPane scrollPane = new JScrollPane(panel); + + int index = 1; + for (Game game : games) { + String white = game.getWhitePlayer().getName(); + String black = game.getBlackPlayer().getName(); + String title = "Spiel " + index++ + ": " + white + " vs. " + black; + JButton btn = new JButton(title); + btn.addActionListener(e -> { + dispose(); + onGameSelected.accept(game); + }); + panel.add(btn); + } + add(scrollPane); + setVisible(true); + } +} + From 6bd35f8e3075795e837fb7ae8bf958576764e5a1 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:38:22 +0200 Subject: [PATCH 25/32] Filling the statsPanel in PgnController --- .../hs_mannheim/informatik/chess/controller/PgnController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java index 9db54b7..fd59baa 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/controller/PgnController.java @@ -65,6 +65,7 @@ public class PgnController { public void updateGuiBoard() { BoardDTO board = engine.getBoardAsDTO(); gui.updateBoard(board); + gui.updateMoveList(engine.getMoveListStringsGrouped()); } private void resetFieldBackground(int row, int col) { From b3af81910a2252c75f8af81fd6fd9718ea08fc31 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:38:52 +0200 Subject: [PATCH 26/32] Implemented the pgn reading feature in MainController --- .../chess/controller/MainController.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) 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 e6e9970..16110d7 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 @@ -2,8 +2,18 @@ package de.hs_mannheim.informatik.chess.controller; import de.hs_mannheim.informatik.chess.view.MainGui; import de.hs_mannheim.informatik.chess.view.PgnGui; +import de.hs_mannheim.informatik.chess.view.PgnSelectionGui; import de.hs_mannheim.informatik.chess.view.CreativeGui; import de.hs_mannheim.informatik.chess.view.GameGui; + +import java.io.IOException; +import java.util.List; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import com.github.bhlangonijr.chesslib.game.Game; + import de.hs_mannheim.informatik.chess.model.ChessEngine; public class MainController { @@ -32,9 +42,23 @@ public class MainController { } private void startLoadGameMode() { - mainGui.close(); - PgnGui pgngui = new PgnGui(); - ChessEngine engine = new ChessEngine(); - new PgnController(pgngui, engine); + JFileChooser chooser = new JFileChooser(); + int result = chooser.showOpenDialog(null); + if (result == JFileChooser.APPROVE_OPTION) { + String path = chooser.getSelectedFile().getAbsolutePath(); + ChessEngine engine = new ChessEngine(); + try { + List games = engine.loadGamesFromPgn(path); + // Jetzt Auswahl-GUI öffnen! + new PgnSelectionGui(games, selectedGame -> { + // Callback wenn User eins ausgewählt hat! + PgnGui pgnGui = new PgnGui(); + engine.loadMoves(selectedGame.getHalfMoves()); + new PgnController(pgnGui, engine); + }); + } catch (IOException ex) { + JOptionPane.showMessageDialog(null, "Fehler beim Laden der PGN-Datei:\n" + ex.getMessage()); + } + } } } \ No newline at end of file From 1dccba2e15cc6817df45b72c274f4b485bd07e30 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 03:39:40 +0200 Subject: [PATCH 27/32] Reworked MainGui class --- .../hs_mannheim/informatik/chess/view/MainGui.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java index 4e40418..74aef54 100644 --- a/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/view/MainGui.java @@ -60,9 +60,15 @@ public class MainGui { } // --- Methoden für Controller zum Setzen der Listener --- - public void setNormalModeListener(ActionListener l) { btnMode1.addActionListener(l); } - public void setCreativeModeListener(ActionListener l) { btnCreative.addActionListener(l); } - public void setLoadGameListener(ActionListener l) { btnLoadGame.addActionListener(l); } + public void setNormalModeListener(ActionListener l) { + btnMode1.addActionListener(l); + } + public void setCreativeModeListener(ActionListener l) { + btnCreative.addActionListener(l); + } + public void setLoadGameListener(ActionListener l) { + btnLoadGame.addActionListener(l); + } public void close() { frame.dispose(); From ffbdb8b6e0c5ad93a777d50a444db77dbc1cb145 Mon Sep 17 00:00:00 2001 From: valen Date: Mon, 23 Jun 2025 13:16:13 +0200 Subject: [PATCH 28/32] =?UTF-8?q?Aktuelles=20Spiel=20kann=20als=20pgn=20an?= =?UTF-8?q?=20gew=C3=BCnschten=20Speicherort,=20mit=20gew=C3=BCnschten=20N?= =?UTF-8?q?amen=20gespeichert=20werden.=20Header=20bis=20auf=20Datum=20ist?= =?UTF-8?q?=20immer=20Gleich?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chess/controller/GameController.java | 21 ++++ .../informatik/chess/model/ChessEngine.java | 98 ++++++++++++++++--- .../informatik/chess/view/GameGui.java | 4 + 3 files changed, 108 insertions(+), 15 deletions(-) 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 c0b4d56..5641c2a 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 @@ -8,6 +8,8 @@ import java.util.List; import javax.swing.BorderFactory; +import com.github.bhlangonijr.chesslib.game.Game; + import de.hs_mannheim.informatik.chess.model.ChessEngine; import de.hs_mannheim.informatik.chess.model.MoveDTO; import de.hs_mannheim.informatik.chess.model.PieceDTO; @@ -68,6 +70,25 @@ public class GameController { engine.setPositionToMoveIndex(engine.getMoveListSize()); updateGuiBoard(); }); + + gui.getBtnSave().addActionListener(e -> { + System.out.println("Save-Button wurde geklickt!"); + Game currentGame = engine.getCurrentGame(); + javax.swing.JFileChooser fileChooser = new javax.swing.JFileChooser(); + fileChooser.setDialogTitle("PGN speichern unter..."); + int userSelection = fileChooser.showSaveDialog(null); + + if (userSelection == javax.swing.JFileChooser.APPROVE_OPTION) { + java.io.File fileToSave = fileChooser.getSelectedFile(); + try { + engine.saveAsPgn(currentGame, fileToSave.getParent(), fileToSave.getName()); + gui.displayMessage("PGN gespeichert: " + fileToSave.getAbsolutePath()); + } catch (Exception ex) { + gui.displayMessage("Fehler beim Speichern: " + ex.getMessage()); + } + } + }); + gui.getFlipBoardButton().addActionListener(e -> { 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 1b9fb78..5ca97cf 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 @@ -14,9 +14,13 @@ import java.util.logging.SimpleFormatter; import com.github.bhlangonijr.chesslib.Board; import com.github.bhlangonijr.chesslib.Piece; import com.github.bhlangonijr.chesslib.Square; -import com.github.bhlangonijr.chesslib.game.Game; import com.github.bhlangonijr.chesslib.move.Move; import com.github.bhlangonijr.chesslib.pgn.PgnHolder; +import com.github.bhlangonijr.chesslib.game.*; +import com.github.bhlangonijr.chesslib.move.MoveList; +import java.time.LocalDate; +import com.github.bhlangonijr.chesslib.Side; + public class ChessEngine { private Board board; @@ -242,14 +246,17 @@ public class ChessEngine { } public void saveAsPgn(Game game, String path, String dateiname) { - String event = game.getRound().getEvent().getName(); - String site = game.getRound().getEvent().getSite(); + // Sicher alle Strings holen (nie null) + String event = safe(game.getRound().getEvent().getName()); + String site = safe(game.getRound().getEvent().getSite()); String round = "" + game.getRound().getNumber(); - String date = game.getRound().getEvent().getStartDate(); - String wName = game.getWhitePlayer().getName(); - String bName = game.getBlackPlayer().getName(); - String result = game.getResult().getDescription(); + // Datum für PGN-Format (YYYY.MM.DD) + String date = safe(game.getRound().getEvent().getStartDate()).replace("-", "."); + String wName = safe(game.getWhitePlayer().getName()); + String bName = safe(game.getBlackPlayer().getName()); + String result = safe(game.getResult().getDescription()); + // PGN-Header zusammenbauen StringBuilder header = new StringBuilder(); header.append("[Event \"" + event + "\"]\n"); header.append("[Site \"" + site + "\"]\n"); @@ -257,29 +264,90 @@ public class ChessEngine { header.append("[Round \"" + round + "\"]\n"); header.append("[White \"" + wName + "\"]\n"); header.append("[Black \"" + bName + "\"]\n"); - header.append("[Result \"" + result + "\"]\n"); - header.append("\n"); + header.append("[Result \"" + result + "\"]\n\n"); - StringBuilder sb = new StringBuilder(); + // Züge als SAN holen + StringBuilder moves = new StringBuilder(); String[] sanArray = game.getHalfMoves().toSanArray(); for (int i = 0; i < sanArray.length; i++) { if (i % 2 == 0) { - sb.append((i / 2 + 1)).append(". "); + moves.append((i / 2 + 1)).append(". "); } - sb.append(sanArray[i]).append(" "); + moves.append(sanArray[i]).append(" "); + // Optional: Zeilenumbruch für Lesbarkeit + // if (i > 0 && i % 8 == 0) moves.append("\n"); } + moves.append(result); // Ergebnis am Ende! - sb.append(result); // Endergebnis muss auch am Ende stehen! - - String file = header.toString() + sb.toString(); + String file = header + moves.toString(); + // Datei schreiben try { Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8); } catch (IOException e) { e.printStackTrace(); } } + + // Hilfsfunktion für Null-Sicherheit + private String safe(String s) { + return s == null ? "?" : s; + } + + public Game getCurrentGame() { + return getCurrentGame(this.board, this.moves, this.currentMoveIndex); + } + + public Game getCurrentGame(Board board, java.util.List moves, int currentMoveIndex) { + // Event und Turnierdaten setzen + Event event = new Event(); + event.setName("Generated Game"); + event.setSite("Local"); + event.setStartDate(LocalDate.now().toString()); // Format: yyyy-MM-dd + + // Runde anlegen + Round round = new Round(event); + round.setNumber(1); + + // Spiel initialisieren + Game game = new Game("1", round); // "1" ist die Game-ID + + // Spieler setzen (deine MyPlayer-Klasse) + game.setWhitePlayer(new MyPlayer("White")); + game.setBlackPlayer(new MyPlayer("Black")); + + // Ergebnis setzen + if (board.isMated()) { + game.setResult(board.getSideToMove() == Side.WHITE ? GameResult.BLACK_WON : GameResult.WHITE_WON); + } else if (board.isStaleMate() || board.isDraw()) { + game.setResult(GameResult.DRAW); + } else { + game.setResult(GameResult.ONGOING); + } + + // Züge übernehmen + MoveList moveList = new MoveList(); + for (Move move : moves) { + moveList.add(move); + } + game.setHalfMoves(moveList); + + // Position auf aktuellen Zug setzen (letzter gespielter Halbzug) + if (currentMoveIndex > 0 && currentMoveIndex <= moveList.size()) { + game.setPosition(currentMoveIndex - 1); + } else { + game.setPosition(moveList.size() - 1); + } + + // FEN setzen: JETZT das aktuelle Board-FEN verwenden! + game.setBoard(new Board()); + game.getBoard().loadFromFen(board.getFen()); + + return game; + } + + public void loadMoves(List moveList) { board = new Board(); // Neues leeres Brett 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 5d9dac3..e418b51 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 @@ -32,6 +32,7 @@ public class GameGui { 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); @@ -179,12 +180,14 @@ public class GameGui { 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 buttonPanel.add(btnFirst); buttonPanel.add(btnPrev); buttonPanel.add(btnNext); buttonPanel.add(btnLast); + buttonPanel.add(btnSave); // Unten ins BorderLayout statsPanel.add(buttonPanel, BorderLayout.SOUTH); @@ -243,6 +246,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 void updateBoard(BoardDTO boardDTO) { PieceDTO[][] board = boardDTO.getBoard(); From ea2c0066fd95ae27c05424697bdb7e1b0a62882b Mon Sep 17 00:00:00 2001 From: valen Date: Mon, 23 Jun 2025 13:16:52 +0200 Subject: [PATCH 29/32] Die Klasse wurde beim letzten "Commi and Push" nicht mit genommen --- .../informatik/chess/model/MyPlayer.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/model/MyPlayer.java diff --git a/schach/src/main/java/de/hs_mannheim/informatik/chess/model/MyPlayer.java b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/MyPlayer.java new file mode 100644 index 0000000..7681ff9 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/MyPlayer.java @@ -0,0 +1,72 @@ +package de.hs_mannheim.informatik.chess.model; + +import com.github.bhlangonijr.chesslib.game.Player; +import com.github.bhlangonijr.chesslib.game.PlayerType; + +public class MyPlayer implements Player { + private String id = ""; + private String name; + private int elo = 0; + private PlayerType type = PlayerType.HUMAN; + private String description = ""; + private String longDescription = ""; + + public MyPlayer(String name) { + this.name = name; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public int getElo() { + return elo; + } + + @Override + public void setElo(int elo) { + this.elo = elo; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public PlayerType getType() { + return type; + } + + @Override + public void setType(PlayerType type) { + this.type = type; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public String getLongDescription() { + return longDescription; + } +} From 268f5e23989d4623f9eba1172aae3c73d49cedb8 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 19:14:01 +0200 Subject: [PATCH 30/32] Implemented timer for testing new chess mode --- .../chess/controller/GameController.java | 33 +++++++- .../informatik/chess/model/ChessEngine.java | 10 +++ .../informatik/chess/model/Timer.java | 81 +++++++++++++++++++ .../informatik/chess/view/GameGui.java | 57 +++++++++++-- 4 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/model/Timer.java 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 c0b4d56..7f6b602 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 @@ -23,10 +23,18 @@ public class GameController { public GameController(GameGui gui, ChessEngine engine) { this.gui = gui; this.engine = engine; + 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; } @@ -172,6 +180,10 @@ public class GameController { } else if (engine.isStalemate() || engine.isDraw()) { gui.displayMessage("Remis! (Stalemate oder andere Regel)"); } + + if (!engine.isMated() && !engine.isStalemate() && !engine.isDraw()) { + switchTimers(); + } } @@ -179,14 +191,33 @@ 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) { + String winner = color.equals("WHITE") ? "SCHWARZ" : "WEIß"; + gui.displayMessage(winner + " hat durch Zeit gewonnen!"); + engine.getWhiteTimer().stop(); + engine.getBlackTimer().stop(); + } private void resetFieldBackground(int row, int col) { Color LIGHT = new Color(0xe0e1dd); 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 1b9fb78..d51554f 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 @@ -24,6 +24,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(); @@ -241,6 +243,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) { String event = game.getRound().getEvent().getName(); String site = game.getRound().getEvent().getSite(); @@ -294,4 +301,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..4ad3738 --- /dev/null +++ b/schach/src/main/java/de/hs_mannheim/informatik/chess/model/Timer.java @@ -0,0 +1,81 @@ +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; + + /** + * Erstellt einen neuen Timer mit Startzeit in Minuten und Sekunden. + */ + public Timer(int minutes, int seconds) { + this.secondsLeft = minutes * 60 + seconds; + } + + /** + * Setzt den Timeout-Callback. + */ + public void setOnTimeout(Runnable onTimeout) { + this.onTimeout = onTimeout; + } + + /** + * Setzt den Tick-Callback (wird jede Sekunde aufgerufen). + */ + public void setOnTick(Consumer onTick) { + this.onTick = onTick; + } + + /** + * Startet oder setzt den Timer fort. + */ + 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(); + } + + /** + * Stoppt den Timer. + */ + public void stop() { + if (swingTimer != null) { + swingTimer.stop(); + } + } + + /** + * Setzt den Timer auf die gegebene Zeit zurück. + */ + public void reset(int minutes, int seconds) { + stop(); + this.secondsLeft = minutes * 60 + seconds; + if (onTick != null) { + onTick.accept(secondsLeft); + } + } + + /** + * Gibt die aktuelle Restzeit in Sekunden zurück. + */ + 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 5d9dac3..e2bdb9f 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 @@ -28,6 +28,9 @@ public class GameGui { private JButton flipBoardButton; private boolean isFlipped = false; + private JLabel whiteTimerLabel; + private JLabel blackTimerLabel; + JButton btnFirst = new JButton("|<"); JButton btnPrev = new JButton("<"); JButton btnNext = new JButton(">"); @@ -160,6 +163,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)); @@ -168,29 +174,46 @@ 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) 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); - - // Hinzufügen buttonPanel.add(btnFirst); buttonPanel.add(btnPrev); buttonPanel.add(btnNext); buttonPanel.add(btnLast); - - // 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"}; @@ -264,6 +287,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); From cdc418afb4de7c23e6c26488af073d63516c9cdb Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 20:47:16 +0200 Subject: [PATCH 31/32] New Callback class to open new GameGui after a chess game --- .../chess/controller/GameController.java | 37 ++++++++++++++++++- .../chess/controller/GameEndCallback.java | 6 +++ .../chess/controller/MainController.java | 12 +++++- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 schach/src/main/java/de/hs_mannheim/informatik/chess/controller/GameEndCallback.java 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 7f6b602..82843d7 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 @@ -13,16 +13,21 @@ 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(); @@ -101,6 +106,9 @@ public class GameController { } private void handleClick(int guiRow, int guiCol) { + + if (gameOver) return; + int modelRow = flipRow(guiRow); int modelCol = flipCol(guiCol); @@ -146,6 +154,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"); @@ -177,8 +188,12 @@ 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()) { @@ -213,12 +228,30 @@ public class GameController { // 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); From 12bea41b1e9b189df4d9e2182b70b3fad854d94d Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 23 Jun 2025 20:47:28 +0200 Subject: [PATCH 32/32] Timer class reworked --- .../informatik/chess/model/Timer.java | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) 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 index 4ad3738..19441d6 100644 --- 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 @@ -7,30 +7,19 @@ public class Timer { private Runnable onTimeout; private Consumer onTick; - /** - * Erstellt einen neuen Timer mit Startzeit in Minuten und Sekunden. - */ + public Timer(int minutes, int seconds) { this.secondsLeft = minutes * 60 + seconds; } - /** - * Setzt den Timeout-Callback. - */ public void setOnTimeout(Runnable onTimeout) { this.onTimeout = onTimeout; } - /** - * Setzt den Tick-Callback (wird jede Sekunde aufgerufen). - */ public void setOnTick(Consumer onTick) { this.onTick = onTick; } - /** - * Startet oder setzt den Timer fort. - */ public void start() { if (swingTimer != null && swingTimer.isRunning()) { swingTimer.stop(); @@ -50,18 +39,12 @@ public class Timer { swingTimer.start(); } - /** - * Stoppt den Timer. - */ public void stop() { if (swingTimer != null) { swingTimer.stop(); } } - /** - * Setzt den Timer auf die gegebene Zeit zurück. - */ public void reset(int minutes, int seconds) { stop(); this.secondsLeft = minutes * 60 + seconds; @@ -70,9 +53,6 @@ public class Timer { } } - /** - * Gibt die aktuelle Restzeit in Sekunden zurück. - */ public int getSecondsLeft() { return secondsLeft; }