Compare commits

...

77 Commits

Author SHA1 Message Date
Justin Muravjev 6f850aa11b Thomas sources for project 2025-06-24 12:10:59 +02:00
Justin Muravjev c9efa6ecda Newer pom.xml 2025-06-24 12:08:39 +02:00
Justin Muravjev 833630abcd Library Quellen 2025-06-24 10:58:45 +02:00
Justin Muravjev 2cf0075c0c Alle Quellen 2025-06-24 10:57:36 +02:00
Justin Muravjev 65c7486a32 Quellen 2025-06-24 10:57:03 +02:00
Justin Muravjev cf870fec57 Merge remote-tracking branch 'origin/Main' into PMD-Korrektur 2025-06-24 10:53:19 +02:00
thomasmuller cc18a20588 pom gefixed 2025-06-24 10:47:00 +02:00
valen 49e88ac03e Korrektur von ChessEngine 2025-06-24 10:26:17 +02:00
thomasmuller cf98d45d6a pom aktualisiert 2025-06-24 10:14:44 +02:00
Justin 32f97aa624 pom rework 2025-06-24 05:50:52 +02:00
Justin fa8f04805f pmd plugin in pom.xml 2025-06-24 05:38:04 +02:00
Justin 59233efcca Last changes for quicksave 2025-06-24 05:31:27 +02:00
Justin aebfb7e38b quicksave feature implemented 2025-06-24 05:04:44 +02:00
Justin d0c267f7b0 Major changes to project 2025-06-24 04:05:46 +02:00
Justin 4cee12cfb9 changes to classes 2025-06-24 04:01:13 +02:00
Justin 8447ce5fe3 Fully implemented creativeMode 2025-06-24 01:47:00 +02:00
Justin 6d3154d0e3 Reworked PgnController and PgnGui 2025-06-24 01:01:26 +02:00
Justin 5770d13cdc Merge remote-tracking branch 'origin/Main' into JUnit-Tests 2025-06-24 00:51:33 +02:00
Justin ae07b4d719 Added undoMove feature and fixed replay issue 2025-06-24 00:50:32 +02:00
Justin aaa4317c9c Added resign and draw feature 2025-06-23 23:44:15 +02:00
valen 3aa8897ff1 Neue Tests hinzugefügt, ControlleTest entfernt(kein mustHave), weil nach
Änderungen ungültig,
2025-06-23 23:25:53 +02:00
Justin 3c3dd5e8ab Added a ComponentListener to the centerPanel in GameGui 2025-06-23 23:15:50 +02:00
Justin 35b7d9aa9a Changed frame size for all Gui and reworked chessPanel in GameGui 2025-06-23 23:11:23 +02:00
Justin 66a8c1a51d New buttons to resign and draw in GameGui 2025-06-23 22:46:15 +02:00
Justin 24374030be Merge branch 'Main' into Openings 2025-06-23 22:00:47 +02:00
Justin b56813d6dc Imported package GameEndCallback 2025-06-23 21:48:37 +02:00
Justin c864f88e1c Updated JUnit branch 2025-06-23 21:47:20 +02:00
Justin a669a4b2ff Reworked and fixed issues with savePgn feature 2025-06-23 21:20:30 +02:00
valen d16aa13804 Openings werden erkannt und im Logger ausgegeben 2025-06-23 21:02:29 +02:00
Justin 12bea41b1e Timer class reworked 2025-06-23 20:47:28 +02:00
Justin cdc418afb4 New Callback class to open new GameGui after a chess game 2025-06-23 20:47:16 +02:00
Justin 268f5e2398 Implemented timer for testing new chess mode 2025-06-23 19:14:01 +02:00
valen ea2c0066fd Die Klasse wurde beim letzten "Commi and Push" nicht mit genommen 2025-06-23 13:16:52 +02:00
valen ffbdb8b6e0 Aktuelles Spiel kann als pgn an gewünschten Speicherort, mit gewünschten
Namen gespeichert werden.
Header bis auf Datum ist immer Gleich
2025-06-23 13:16:13 +02:00
Justin 1dccba2e15 Reworked MainGui class 2025-06-23 03:39:40 +02:00
Justin b3af81910a Implemented the pgn reading feature in MainController 2025-06-23 03:38:52 +02:00
Justin 6bd35f8e30 Filling the statsPanel in PgnController 2025-06-23 03:38:22 +02:00
Justin 2e6b9ec81c New implemented class PgnSelectionGui 2025-06-23 03:38:02 +02:00
Justin f3079dc82e Implemented missing librarys 2025-06-23 03:03:49 +02:00
Justin 24f6339b06 Valentins pgn methods for saving and loading pgn files in ChessEngine 2025-06-23 03:01:16 +02:00
Justin 0d37de1cd9 Removed unused methods in PgnController 2025-06-23 02:57:05 +02:00
Justin 0f5e656614 Added PgnGui to MainController 2025-06-23 02:45:34 +02:00
Justin 778db90bc5 Added all methods to PgnController 2025-06-23 02:43:46 +02:00
Justin 6273261fd0 Added methods to PgnGui class 2025-06-23 02:40:30 +02:00
Justin f80fb99ba1 Reworked whole creativeMode 2025-06-23 02:28:59 +02:00
Justin 6b4cbda250 MainController now has a fully implemented CreativeGui 2025-06-23 01:37:09 +02:00
Justin 5dcdabb215 Fully implemented CreativeGui class 2025-06-23 01:35:57 +02:00
Justin a36fb3b69d Fully implemented CreativeController class 2025-06-23 01:35:43 +02:00
Justin 5d9f4e2b8e New Method setPositionFromFEN in ChessEngine 2025-06-23 01:34:13 +02:00
Justin 4ee2e021ed New PgnController class 2025-06-23 01:01:35 +02:00
Justin 0c5066fd62 New CreativeController class 2025-06-23 01:01:06 +02:00
Justin 4fe9a8ad8a Fixed wrong chessboard square colors 2025-06-23 00:48:30 +02:00
Justin 532059ba0b New PgnGui class 2025-06-23 00:22:43 +02:00
Justin fcab410a2f New CreativeGui class 2025-06-23 00:22:13 +02:00
Justin 485b3d6e14 Initializing new MainController in Main 2025-06-23 00:08:48 +02:00
Justin 519851f46b Reworked MainController 2025-06-23 00:04:50 +02:00
Justin bfbb878647 Removed unused buttons in MainGui 2025-06-23 00:04:13 +02:00
Justin d1feea3fe5 Changed name Controller to GameController 2025-06-22 23:48:40 +02:00
Justin dac360d411 Reworked handleMove for pawn promotion feature 2025-06-22 23:13:07 +02:00
Justin 31a917d586 New method moveWithPromotion implemented in ChessEngine 2025-06-22 23:04:20 +02:00
Justin f72883710f New method showPromotionDialog in GameGui 2025-06-22 23:02:28 +02:00
Justin d23f7097f7 Merge branch 'Main' into JUnit-Tests 2025-06-22 15:26:18 +02:00
valen 69c4e44671 pom.xml neue Änderung 2025-06-22 15:22:29 +02:00
valen 7ebfecc6c4 ModelTests wurden hinzugefügt 2025-06-22 15:19:08 +02:00
valen 993fa89371 pom.xml Anderung 2025-06-22 15:17:50 +02:00
valen 9e095418a2 Tests fast vollständig 2025-06-22 15:07:06 +02:00
Justin 3e1a770176 New buttons in MainGui "Creative Mode" and "Load Game(PGN)" 2025-06-22 03:12:07 +02:00
Justin 1232345b00 Changed code comments in Controller 2025-06-22 03:06:44 +02:00
Justin 0d899a762a New method logging with customized logging in ChessEngine 2025-06-22 02:36:55 +02:00
Justin e0d14e0811 Reworked methods and added a logger to ChessEngine 2025-06-22 02:24:26 +02:00
Justin 3bad46f9ea Fixed undoMove feature in ChessEngine 2025-06-22 01:58:47 +02:00
Justin 7e4ac22e51 Added button-listener to Controller 2025-06-22 01:41:45 +02:00
Justin 3335dc75cf New method getMoveListSize in ChessEngine 2025-06-22 01:32:23 +02:00
Justin 572e7b1131 New method getCurrentMoveIndex in ChessEngine 2025-06-22 01:31:18 +02:00
Justin 745bf05fd3 New method setPositionToMoveIndex for undo move feature in ChessEngine 2025-06-22 01:30:21 +02:00
Justin 0bc99a18be Getter for undo-buttons 2025-06-22 01:28:37 +02:00
valen 4c58eebc65 JUnit-Test-Klasse angelegt 2025-06-21 14:11:10 +02:00
24 changed files with 6446 additions and 424 deletions

View File

@ -0,0 +1,259 @@
1. Methode toPGN ist vorhanden in Klasse Game.java!!! Mit MoveGenerator werden schon alle legalen züge angezeigt!!!
2. Klassen von GPT zusammengefasst:
https://chatgpt.com/share/684326dc-4ea0-8012-8c10-8b34993140b7
Event:
Die Klasse Event beschreibt ein Schachturnier oder -ereignis mit Name, Ort, Datum, Zeitkontrollen, Rundenstruktur und referenzierten PGN-Daten.
EventType:
EventType beschreibt den strukturellen Typ eines Schachturniers (z.B. KO-System, Rundenturnier oder Analyseveranstaltung).
Game:
Die Klasse Game speichert und verwaltet alle Informationen einer Schachpartie wie Züge, Spieler, Kommentare und Metadaten und erlaubt deren Analyse, Bearbeitung und Ausgabe im PGN-Format.
GameContext:
Die Klasse GameContext stellt alle benötigten Informationen und Methoden bereit, um Rochaden und Schachvarianten (z.B. Chess960) regelkonform zu verwalten und korrekt zu validieren.
GameFactory:
GameFactory stellt statische Hilfsmethoden bereit, um neue Schachobjekte (Spiel, Spieler, Runde, Event) schnell und einheitlich zu erzeugen.
GameMode:
GameMode legt fest, ob eine Partie zwischen Menschen, Maschinen oder gemischt (z.B. Engine vs Mensch) gespielt wird.
GameResult:
GameResult beschreibt, wie eine Schachpartie geendet hat (Sieg, Remis oder noch laufend) und verknüpft dies mit der entsprechenden PGN-Notation.
MoveGeneratorException
MoveGeneratorException signalisiert Fehler beim Generieren von Schachzügen und erlaubt dabei flexible Fehlerbeschreibungen.
MoveList
MoveList verwaltet eine Folge von Schachzügen und erlaubt deren Umwandlung, Validierung und Darstellung in verschiedenen Notationen basierend auf einer Ausgangsstellung.
GameLoader:
Die Klasse GameLoader lädt eine vollständige Schachpartie inklusive aller Metadaten und Züge aus einem PGN-Dateiiterator und wandelt sie in ein Game-Objekt um.
GenericPlayer:
Die Klasse GenericPlayer implementiert das Player-Interface und speichert die wichtigsten Eigenschaften eines Schachspielers wie Name, Elo, Typ (Mensch/Maschine) und Beschreibung.
Mode:
Mode beschreibt den Austragungsort einer Partie: entweder als Präsenzspiel am Brett (OTB) oder über einen Online-Server (ICS).
MovePerTime:
MovePerTime beschreibt eine Zeitkontrolle, bei der eine bestimmte Anzahl an Zügen in einer definierten Zeit (in Millisekunden) gespielt werden muss, z.B. 40/5400 (40 Züge in 90 Minuten).
Player:
Player ist ein Interface, das die grundlegenden Eigenschaften und Methoden eines Schachspielers festlegt, darunter ID, Name, Elo, Typ und eine ausführliche Beschreibung.
PlayerType:
PlayerType unterscheidet, ob ein Spieler ein Mensch (HUMAN) oder eine Engine (ENGINE) ist.
Round:
Round speichert eine bestimmte Runde eines Schachturniers und enthält eine Liste aller Partien (Game), die in dieser Runde gespielt werden.
Termination:
Termination gibt an, warum eine Partie beendet wurde, z.B. regulär, durch Zeitüberschreitung, Regelverstoß oder Abbruch.
TimeControl:
TimeControl speichert verschiedene Zeitregelungen für eine Partie etwa klassische Zeitkontrollen, Bonuszeiten (Inkrement), feste Zugvorgaben, Knoten-/Tiefenlimits (für Engines) und gibt sie als PGN-kompatible Zeichenkette zurück.
TimeControlType:
TimeControlType gibt an, in welchem Modus eine Zeitkontrolle funktioniert, z.B. klassisches Zeitlimit mit Inkrement, Zeit pro Zug, feste Tiefe oder Knotenzahl (für Engines).
VariationType:
VariationType gibt an, welche Schachvariante gespielt wird z.B. Standard-Schach, Chess960 oder Varianten mit speziellen Rochaderegeln oder Figurenmechaniken.
Move:
Move repräsentiert einen einzelnen Schachzug mit Start- und Zielfeld, optionaler Bauernumwandlung sowie SAN-Notation, und kann als Ereignis (BoardEvent) weiterverarbeitet werden.
MoveConversionException:
MoveConversionException signalisiert, dass ein Zug nicht korrekt aus einem Text (z.B. "e2e99" oder "z9z8Q") in ein gültiges Move-Objekt umgewandelt werden konnte.
MoveException:
MoveException signalisiert, dass ein Zug nicht auf dem aktuellen Spielstand ausgeführt werden kann typischerweise, weil er regelwidrig, unmöglich oder logisch inkonsistent ist.
MoveGenerator:
MoveGenerator stellt statische Hilfsmethoden bereit, um aus einem gegebenen Schachbrettzustand alle möglichen Züge (pseudo-legal oder legal) zu erzeugen, einschließlich Spezialzüge wie Rochade oder Bauernumwandlungen.
PgnException
Die Klasse PgnException signalisiert Laufzeitfehler beim Parsen oder Einlesen von PGN-Schachpartien.
PgnHolder
Die Klasse PgnHolder verwaltet das Einlesen, Speichern und Abrufen von Schachpartien aus PGN-Dateien und organisiert dabei Spieler, Events und Spiele zentral in einer leicht zugänglichen Struktur.
PgnIterator
Die Klasse PgnIterator ermöglicht das speicherschonende Durchiterieren von Schachpartien in einer PGN-Datei, indem sie zeilenweise liest und jede Partie einzeln verarbeitet.
PgnLoadListener
Die Schnittstelle PgnLoadListener erlaubt es, auf Fortschritte beim Laden von PGN-Partien zu reagieren, indem sie bei jedem Ladefortschritt benachrichtigt wird.
PgnProperty
Die Klasse PgnProperty modelliert eine einzelne PGN-Eigenschaft (Tag) und stellt Hilfsmethoden bereit, um sie aus einer Textzeile zu erkennen und zu extrahieren.
UnicodePrinter
Die Klasse UnicodePrinter druckt ein Schachbrett-Objekt (Board) als ASCII-Art mit Unicode-Schachsymbolen zeilenweise in die Konsole oder in einen beliebigen Ausgabestrom.
LargeFile
Die Klasse LargeFile erlaubt das effiziente und speicherschonende zeilenweise Lesen großer Textdateien oder Eingabeströme durch Bereitstellung eines einfachen Java-Iterators.
StringUtil
Die Klasse StringUtil stellt nützliche Methoden zur Verfügung, um Zeichenketten effizient zu analysieren, zu verändern und zu übersetzen insbesondere bei der Verarbeitung von Schach-PGN-Daten.
XorShiftRandom
Die Klasse XorShiftRandom implementiert einen leistungsfähigen, einfachen Pseudozufallszahlengenerator auf Basis des Xorshift-Verfahrens, der deterministische long-Werte durch Bitoperationen erzeugt.
Bitboard
Die Klasse Bitboard stellt für jedes Schachfeld und diverse Feldergruppen (Reihen, Linien, Diagonalen) Bitmasken bereit, um Schachpositionen effizient mit Bitoperationen zu verarbeiten.
Board
Die Klasse Board modelliert ein vollständiges Schachbrett inklusive Spielregeln, Figurenpositionen, Zugverlauf und ermöglicht die Validierung, Durchführung und Rücknahme von Schachzügen.
BoardEvent
Das Interface BoardEvent definiert die Struktur für Ereignisse, die beim Ändern des Schachbretts ausgelöst und an Listener weitergegeben werden können.
BoardEventListener
Das Interface BoardEventListener erlaubt es, auf Ereignisse wie Züge oder Statusänderungen des Schachbretts zu reagieren, indem es über die Methode onEvent() benachrichtigt wird.
BoardEventType
Das Enum BoardEventType definiert die Arten von Ereignissen, die auf einem Schachbrett ausgelöst und über Event-Listener verarbeitet werden können etwa beim Ziehen, Zurücknehmen oder Laden.
CastleRight
Das Enum CastleRight beschreibt, ob eine Seite im Schach kurz, lang, beides oder gar nicht rochieren darf.
Constants
Die Klasse Constants bündelt feste, zentrale Informationen und Züge wie die Start-FEN, Rochadepfade und Figur-Notation, um typische Schachsituationen schnell und konsistent abrufen zu können.
DiagonalA1H8
Die Enum DiagonalA1H8 listet alle rechtsschiefen (↘️) Diagonalen des Schachbretts als konstante Werte mit eindeutiger Namensgebung der Begrenzungsfelder auf.
DiagonalH1A8
Die Enum DiagonalH1A8 listet alle linksschiefen (↙️) Diagonalen des Schachbretts auf, wobei jede Diagonale durch ihre Randfelder eindeutig benannt ist.
File
Das Enum File beschreibt die Spalten (AH) des Schachbretts samt zugehöriger Notation und ermöglicht deren einfache Nutzung in Code und Notationen.
MoveBackup
Die Klasse MoveBackup speichert alle relevanten Zustandsinformationen eines Schachbretts vor einem Zug, um diesen später vollständig rückgängig machen zu können.
Piece
Die Klasse Piece repräsentiert jede konkrete Schachfigur mit Farbe und Typ und bietet Methoden zur Erstellung und Notation dieser Figuren.
PieceType
PieceType beschreibt die Typen von Schachfiguren (z.B. Turm, König) und verknüpft sie mit ihrer algebraischen Notation für einfache Identifikation und Verarbeitung.
Rank
Rank beschreibt die horizontalen Reihen des Schachbretts und stellt deren numerische Notation zur Verfügung, um Positionen eindeutig identifizieren zu können.
Side
Side repräsentiert eine der beiden Spielerparteien im Schach Weiß oder Schwarz und ermöglicht den einfachen Wechsel der Seite mit der Methode flip().
Square
Square ist ein mächtiges enum, das jedes Schachfeld eindeutig repräsentiert und dabei zahlreiche Hilfsmethoden zur Brettlogik und Bitboard-Darstellung bereitstellt. Es ist eine der Grundpfeiler des gesamten Schachsystems.

View File

@ -0,0 +1,21 @@
Quellen
Thomas Müller | 3021788
Timer https://chatgpt.com/c/6857ff1e-7ff4-8006-aba3-60d3a2dd59b4
Valentin Weller | 3019075
Junit: https://chatgpt.com/share/6859a713-4b98-8012-8cb2-a9f3c7fb37c8
QuickSave: https://chatgpt.com/share/6859a73f-e750-8012-a168-f9fa7c5487bd
PGNs: https://chatgpt.com/share/6859a8f1-d3c4-8012-98f4-6ae4b057be73
https://chatgpt.com/share/6859a95a-cca8-8012-bbbe-4e4b4517d5d9
Eröffnungserkennung: https://github.com/hell-sh/CompactChessOpenings/blob/master/src/sh/hell/compactchess/game/Opening.java
Quellen
Justin Muravjev 3014931
ChatGPT:
https://chatgpt.com/share/685a60b4-dad0-8012-86a8-ca9a7e6eb76b

File diff suppressed because it is too large Load Diff

View File

@ -10,13 +10,12 @@
<version>0.0.1-SNAPSHOT</version>
<name>schach</name>
<description>A simple schach.</description>
<description>A simple schach project</description>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<repositories>
@ -27,22 +26,31 @@
</repositories>
<dependencies>
<!-- Schachlib (bhlangonijr) -->
<!-- Schachlib -->
<dependency>
<groupId>com.github.bhlangonijr</groupId>
<artifactId>chesslib</artifactId>
<version>1.3.4</version>
</dependency>
<!-- JUnit 3 (wie im Template, ggf. auf JUnit 4/5 upgraden für moderne Projekte) -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
@ -83,13 +91,59 @@
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- Java Compiler Plugin -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<!-- PMD Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.21.0</version>
<configuration>
<!-- Set to 17 until PMD supports 21 -->
<targetJdk>17</targetJdk>
<failOnViolation>false</failOnViolation>
<printFailingErrors>true</printFailingErrors>
<linkXRef>false</linkXRef>
<rulesets>
<ruleset>rulesets/java/quickstart.xml</ruleset>
<ruleset>rulesets/java/basic.xml</ruleset>
<ruleset>rulesets/java/braces.xml</ruleset>
<ruleset>rulesets/java/unusedcode.xml</ruleset>
<ruleset>rulesets/java/imports.xml</ruleset>
</rulesets>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Surefire Plugin for tests -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.6.1</version>
</plugin>
</plugins>
</reporting>
</project>
</project>

View File

@ -1,156 +0,0 @@
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.GameGui;
public class Controller {
GameGui gui;
ChessEngine engine;
private int selectedRow = -1, selectedCol = -1;
private List<int[]> highlightedFields = new ArrayList<>();
public Controller(GameGui gui, ChessEngine engine) {
this.gui = gui;
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);
}
});
}
}
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<MoveDTO> 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;
}
}
public void handleMove(MoveDTO move) {
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!");
}
}
public void updateGuiBoard() {
BoardDTO board = engine.getBoardAsDTO();
gui.updateBoard(board); // Passe die GUI an
}
// 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;
}
// ... resetFieldBackground wie gehabt ...
private void resetFieldBackground(int row, int col) {
if ((row + col) % 2 == 0) {
gui.getField(row, col).setBackground(new Color(0x778da9));
} else {
gui.getField(row, col).setBackground(new Color(0xe0e1dd));
}
}
}

View File

@ -0,0 +1,169 @@
package de.hs_mannheim.informatik.chess.controller;
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<String, Character> PIECE_TO_FEN = createPieceToFenMap();
private static HashMap<String, Character> createPieceToFenMap() {
HashMap<String, Character> map = new HashMap<String, Character>();
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("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
applyFenToBoard();
}
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);
}
});
}
}
gui.getFlipBoardButton().addActionListener(e -> {
applyFenToBoard();
gui.setFlipped(!gui.isFlipped());
updateGuiFromEngine(); // Board wird neu angezeigt, ggf. invertiert
});
}
private void fieldClicked(int row, int col) {
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();
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<String, String> 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() {
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[displayRow][displayCol].setText("");
} else {
for (Map.Entry<String, String> entry : CreativeGui.UNICODE_MAP.entrySet()) {
if (entry.getKey().startsWith(p.getColor()) && entry.getKey().endsWith(p.getType())) {
fields[displayRow][displayCol].setText(entry.getValue());
break;
}
}
}
}
}
}
}

View File

@ -0,0 +1,438 @@
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 javax.swing.JOptionPane;
import com.github.bhlangonijr.chesslib.game.Game;
import de.hs_mannheim.informatik.chess.model.ChessEngine;
import de.hs_mannheim.informatik.chess.model.GameMode;
import de.hs_mannheim.informatik.chess.model.MoveDTO;
import de.hs_mannheim.informatik.chess.model.PieceDTO;
import de.hs_mannheim.informatik.chess.model.Timer;
import de.hs_mannheim.informatik.chess.model.BoardDTO;
import de.hs_mannheim.informatik.chess.view.GameGui;
public class GameController {
GameGui gui;
ChessEngine engine;
GameEndCallback callback;
private boolean gameOver = false;
private int selectedRow = -1;
private int selectedCol = -1;
private List<int[]> highlightedFields = new ArrayList<>();
private boolean gameWasResignedOrDrawn = false;
private GameMode gameMode;
public GameController(GameGui gui, ChessEngine engine, GameEndCallback callback, GameMode gameMode) {
this(gui, engine, callback); // ruft anderen Konstruktor auf
this.gameMode = gameMode;
if (gameMode != null) {
engine.initTimers(gameMode.minutes, gameMode.incrementSeconds);
setupAndStartTimers();
}
addWindowCloseListener();
}
// Für Creative/PGN-Mode (ohne Zeit)
public GameController(GameGui gui, ChessEngine engine, GameEndCallback callback) {
this.gui = gui;
this.engine = engine;
this.callback = callback;
this.gameMode = null;
addWindowCloseListener();
// KEINE Timer initialisieren
initListeners();
updateGuiBoard();
}
private void setupAndStartTimers() {
if (engine.getWhiteTimer() != null) {
engine.getWhiteTimer().setOnTick(secs -> gui.updateWhiteTimerLabel(secs));
engine.getWhiteTimer().setOnTimeout(() -> onTimeout("WHITE"));
engine.getWhiteTimer().stop(); // <-- WICHTIG!
}
if (engine.getBlackTimer() != null) {
engine.getBlackTimer().setOnTick(secs -> gui.updateBlackTimerLabel(secs));
engine.getBlackTimer().setOnTimeout(() -> onTimeout("BLACK"));
engine.getBlackTimer().stop(); // <-- WICHTIG!
}
// Timer-Labels initial setzen
if (engine.getWhiteTimer() != null) gui.updateWhiteTimerLabel(engine.getWhiteTimer().getSecondsLeft());
if (engine.getBlackTimer() != null) gui.updateBlackTimerLabel(engine.getBlackTimer().getSecondsLeft());
// Aktuellen Spieler-Timer starten:
if (engine.getCurrentPlayer().equals("WHITE")) {
engine.getWhiteTimer().start();
} else {
engine.getBlackTimer().start();
}
}
private int flipRow(int row) {
return gui.isFlipped() ? 7 - row : row;
}
private int flipCol(int col) {
return gui.isFlipped() ? 7 - col : col;
}
private void addWindowCloseListener() {
if (gui.getFrame() != null) {
gui.getFrame().addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
if (!gameOver) {
engine.quicksave();
MainController.engineRAM = engine;
}
new MainController();
}
});
}
}
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.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 -> {
//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();
});
// --- AUFGEBEN-BUTTON ---
gui.getResignButton().addActionListener(e -> {
if (gameOver) return;
int answer = JOptionPane.showConfirmDialog(
null,
"Willst du wirklich aufgeben?",
"Aufgeben",
JOptionPane.YES_NO_OPTION
);
if (answer == JOptionPane.YES_OPTION) {
gameOver = true;
gameWasResignedOrDrawn = true; // <<<<<<
String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß";
gui.displayMessage(winner + " gewinnt durch Aufgabe!");
if (engine.getWhiteTimer() != null) engine.getWhiteTimer().stop();
if (engine.getBlackTimer() != null) engine.getBlackTimer().stop();
askForRestart();
}
});
gui.getDrawButton().addActionListener(e -> {
if (gameOver) return;
int answer = JOptionPane.showConfirmDialog(
null,
"Remis anbieten? (Das Spiel endet sofort unentschieden)",
"Remis",
JOptionPane.YES_NO_OPTION
);
if (answer == JOptionPane.YES_OPTION) {
gameOver = true;
gameWasResignedOrDrawn = true; // <<<<<<
gui.displayMessage("Remis! (durch Einigung)");
if (engine.getWhiteTimer() != null) engine.getWhiteTimer().stop();
if (engine.getBlackTimer() != null) engine.getBlackTimer().stop();
askForRestart();
}
});
gui.getUndoButton().addActionListener(e -> {
// Wer ist am Zug? (Das ist der, der gefragt wird)
String currentPlayer = engine.getCurrentPlayer(); // "WHITE" oder "BLACK"
// Wer möchte zurücknehmen? (Das ist der, der NICHT am Zug ist)
String currentName = currentPlayer.equals("WHITE") ? "Weiß" : "Schwarz";
String previousName = currentPlayer.equals("WHITE") ? "Schwarz" : "Weiß";
int answer = javax.swing.JOptionPane.showConfirmDialog(
null,
currentName + " ist am Zug. " +
previousName + " möchte seinen letzten Zug zurücknehmen.\n" +
currentName + ", erlaubst du das?",
"Zug zurücknehmen?",
javax.swing.JOptionPane.YES_NO_OPTION
);
if (answer == javax.swing.JOptionPane.YES_OPTION) {
engine.undoLastMove();
updateGuiBoard();
gui.updateMoveList(engine.getMoveListStringsGrouped());
gui.setOpeningLabel(engine.getOpeningName());
gui.displayMessage("Der letzte Zug wurde zurückgenommen.");
} else if (answer == javax.swing.JOptionPane.NO_OPTION) {
gui.displayMessage("Das Zurücknehmen wurde abgelehnt.");
}
});
}
private boolean isAtLatestMove() {
return engine.getCurrentMoveIndex() == engine.getMoveListSize();
}
private void handleClick(int guiRow, int guiCol) {
if (gameOver) return;
if (!isAtLatestMove()) {
gui.displayMessage("Du bist im Zug-Archiv und kannst keine Figuren bewegen. Klick auf '>|', um zum letzten Zug zu gehen.");
return;
}
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<MoveDTO> 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) {
if (gameOver) return;
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)!");
gameOver = true;
askForRestart();
} else if (engine.isStalemate() || engine.isDraw()) {
gui.displayMessage("Remis! (Stalemate oder andere Regel)");
gameOver = true;
askForRestart();
}
if (!engine.isMated() && !engine.isStalemate() && !engine.isDraw()) {
switchTimers();
}
}
public void updateGuiBoard() {
BoardDTO board = engine.getBoardAsDTO();
gui.updateBoard(board);
gui.setOpeningLabel(engine.getOpeningName());
}
private void switchTimers() {
GameMode mode = engine.getGameMode();
Timer whiteTimer = engine.getWhiteTimer();
Timer blackTimer = engine.getBlackTimer();
// Wenn KEIN Modus (also kein Timer): NICHTS machen!
if (mode == null || whiteTimer == null || blackTimer == null) return;
if (engine.getCurrentPlayer().equals("WHITE")) {
if (mode.incrementSeconds > 0) {
blackTimer.addSeconds(mode.incrementSeconds);
}
blackTimer.stop();
whiteTimer.start();
} else {
if (mode.incrementSeconds > 0) {
whiteTimer.addSeconds(mode.incrementSeconds);
}
whiteTimer.stop();
blackTimer.start();
}
}
// Hilfsmethode, um von Koordinaten (row/col) auf z.B. "E2" zu kommen
private String coordToChessNotation(int modelRow, int modelCol) {
char file = (char)('A' + modelCol);
int rank = 8 - modelRow;
return "" + file + rank;
}
// Timeout-Methode
private void onTimeout(String color) {
if (gameOver) return; // Doppelt hält besser
gameOver = true;
String winner = color.equals("WHITE") ? "SCHWARZ" : "WEIß";
gui.displayMessage(winner + " hat durch Zeit gewonnen!");
engine.getWhiteTimer().stop();
engine.getBlackTimer().stop();
askForRestart();
}
private void askForRestart() {
int answer = JOptionPane.showConfirmDialog(
null,
"Neue Partie starten?",
"Spiel beendet",
JOptionPane.YES_NO_OPTION
);
javax.swing.SwingUtilities.getWindowAncestor(gui.getField(0, 0)).dispose();
if (answer == JOptionPane.YES_OPTION) {
engine.clearQuicksave();
MainController.engineRAM = null;
callback.onNewGameRequested();
} else {
// HIER: QuickSave **NUR** falls das Spiel NICHT durch Aufgabe/Remis/Patt beendet wurde!
if (!gameWasResignedOrDrawn) {
engine.quicksave();
MainController.engineRAM = engine;
} else {
engine.clearQuicksave();
MainController.engineRAM = null;
}
callback.onReturnToMenu();
}
gameWasResignedOrDrawn = false; // Reset für nächstes Spiel
}
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);
}
}
}

View File

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

View File

@ -0,0 +1,131 @@
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 de.hs_mannheim.informatik.chess.view.GameModeSelector;
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;
import de.hs_mannheim.informatik.chess.model.GameMode;
public class MainController {
private MainGui mainGui;
static ChessEngine engineRAM = null;
public MainController() {
mainGui = new MainGui();
mainGui.setNormalModeListener(e -> startNormalMode());
mainGui.setCreativeModeListener(e -> startCreativeMode());
mainGui.setLoadGameListener(e -> startLoadGameMode());
}
private void startNormalMode() {
// Prüfe, ob es im RAM ein Quicksave gibt:
if (engineRAM != null && engineRAM.quickload()) {
int choice = JOptionPane.showConfirmDialog(
null,
"Letzte Partie fortsetzen?",
"Quicksave gefunden",
JOptionPane.YES_NO_OPTION
);
if (choice == JOptionPane.YES_OPTION) {
GameGui gameGui = new GameGui();
GameEndCallback callback = new GameEndCallback() {
public void onNewGameRequested() { startNormalMode(); }
public void onReturnToMenu() { new MainController(); }
};
new GameController(gameGui, engineRAM, callback); // KEIN initTimers, KEIN neuer Engine!
// --- Timerlabels aktualisieren
gameGui.updateWhiteTimerLabel(engineRAM.getWhiteTimer().getSecondsLeft());
gameGui.updateBlackTimerLabel(engineRAM.getBlackTimer().getSecondsLeft());
// --- Timer des aktuellen Spielers starten
if (engineRAM.getCurrentPlayer().equals("WHITE")) {
engineRAM.getWhiteTimer().start();
} else {
engineRAM.getBlackTimer().start();
}
mainGui.close();
return; // Fertig!
} else {
engineRAM.clearQuicksave();
engineRAM = null;
}
}
// Neues Spiel normal starten:
GameMode mode = GameModeSelector.selectGameMode();
if (mode == null) return;
mainGui.close();
ChessEngine engine = new ChessEngine(mode);
engineRAM = engine; // Für spätere Quicksaves merken!
GameGui gameGui = new GameGui();
GameEndCallback callback = new GameEndCallback() {
public void onNewGameRequested() {
startNormalMode();
}
public void onReturnToMenu() {
new MainController();
}
};
new GameController(gameGui, engine, callback, mode);
}
private void startCreativeMode() {
mainGui.close();
CreativeGui creativegui = new CreativeGui();
ChessEngine engine = new ChessEngine();
new CreativeController(creativegui, engine);
creativegui.setStartGameCallback(fen -> {
// 1. Modus-Auswahl-Dialog zeigen!
GameMode mode = GameModeSelector.selectGameMode(); // (Dialog, wie beim Normal Mode)
if (mode == null) return; // User hat abgebrochen → nichts machen
ChessEngine newEngine = new ChessEngine(mode); // Engine mit Modus (Timer)
newEngine.setPositionFromFEN(fen);
GameGui gameGui = new GameGui();
GameEndCallback callback = new GameEndCallback() {
public void onNewGameRequested() { startCreativeMode(); }
public void onReturnToMenu() { new MainController(); }
};
new GameController(gameGui, newEngine, callback, mode); // mit Timer/ohne je nach Modus
creativegui.close();
});
}
private void startLoadGameMode() {
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<Game> 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());
}
}
mainGui.close();
}
}

View File

@ -0,0 +1,83 @@
package de.hs_mannheim.informatik.chess.controller;
import java.awt.Color;
import de.hs_mannheim.informatik.chess.model.ChessEngine;
import de.hs_mannheim.informatik.chess.model.BoardDTO;
import de.hs_mannheim.informatik.chess.view.PgnGui;
public class PgnController {
PgnGui gui;
ChessEngine engine;
public PgnController(PgnGui pgngui, ChessEngine engine) {
this.gui = pgngui;
this.engine = engine;
initListeners();
updateGuiBoard();
}
private void initListeners() {
// 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);
}
}
// 2. Flip-Zustand ändern
gui.setFlipped(!gui.isFlipped());
// 3. Board neu zeichnen
updateGuiBoard();
});
}
public void updateGuiBoard() {
BoardDTO board = engine.getBoardAsDTO();
gui.updateBoard(board);
gui.updateMoveList(engine.getMoveListStringsGrouped());
gui.setOpeningLabel(engine.getOpeningName());
}
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);
}
}
}

View File

@ -1,19 +1,12 @@
package de.hs_mannheim.informatik.chess.main;
import de.hs_mannheim.informatik.chess.controller.Controller;
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 Controller(gui, engine);
});
new MainController();
}
}

View File

@ -1,118 +1,524 @@
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.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
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.move.Move;
import com.github.bhlangonijr.chesslib.pgn.PgnHolder;
import com.github.bhlangonijr.chesslib.game.*;
import com.github.bhlangonijr.chesslib.move.MoveList;
import com.github.bhlangonijr.chesslib.Side;
public class ChessEngine {
private Board board;
private List<Move> moves = new ArrayList<>();
public ChessEngine() {
board = new Board();
}
public boolean move(MoveDTO move) {
String from = "" + (char)('A' + move.getFromCol()) + (8 - move.getFromRow());
String to = "" + (char)('A' + move.getToCol()) + (8 - move.getToRow());
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
if (board.legalMoves().contains(libMove)) {
board.doMove(libMove);
moves.add(libMove); // <-- hier merken!
return true;
}
return false;
}
public List<MoveDTO> getLegalDestinations(String from) {
List<MoveDTO> destinations = new ArrayList<>();
Square fromSq = Square.valueOf(from.toUpperCase());
for (Move move : board.legalMoves()) {
if (move.getFrom().equals(fromSq)) {
int fromRow = 8 - fromSq.getRank().ordinal() - 1;
int fromCol = fromSq.getFile().ordinal();
int toRow = 8 - move.getTo().getRank().ordinal() - 1;
int toCol = move.getTo().getFile().ordinal();;
destinations.add(new MoveDTO(fromRow, fromCol, toRow, toCol));
}
}
return destinations;
}
public List<String> getMoveListStringsGrouped() {
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < moves.size(); i++) {
if (i % 2 == 0) sb.append((i/2 + 1) + ". ");
sb.append(moves.get(i).toString()).append(" ");
if (i % 2 == 1 || i == moves.size() - 1) {
result.add(sb.toString().trim());
sb = new StringBuilder();
}
}
return result;
}
public PieceDTO getPieceAt(String square) {
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase()));
return convertPieceToDTO(piece);
}
public BoardDTO getBoardAsDTO() {
PieceDTO[][] dtoBoard = new PieceDTO[8][8];
for (int rank = 8; rank >= 1; rank--) {
for (int file = 0; file < 8; file++) {
Square square = Square.valueOf("" + (char)('A' + file) + rank);
Piece piece = board.getPiece(square);
dtoBoard[8-rank][file] = convertPieceToDTO(piece);
}
}
return new BoardDTO(dtoBoard);
}
private String pieceToUnicode(Piece piece) {
switch (piece) {
case WHITE_KING: return "♔";
case WHITE_QUEEN: return "♕";
case WHITE_ROOK: return "♖";
case WHITE_BISHOP: return "♗";
case WHITE_KNIGHT: return "♘";
case WHITE_PAWN: return "♙";
case BLACK_KING: return "♚";
case BLACK_QUEEN: return "♛";
case BLACK_ROOK: return "♜";
case BLACK_BISHOP: return "♝";
case BLACK_KNIGHT: return "♞";
case BLACK_PAWN: return "♟";
default: return " ";
}
}
private PieceDTO convertPieceToDTO(Piece piece) {
if (piece == null || piece.equals(Piece.NONE)) return null;
String color = piece.name().startsWith("WHITE") ? "WHITE" : "BLACK";
String type = piece.name().substring(piece.name().indexOf('_') + 1); // "PAWN", "KING"...
String symbol = pieceToUnicode(piece);
return new PieceDTO(type, color, symbol);
}
public boolean isMated() {
return board.isMated();
}
public boolean isStalemate() {
return board.isStaleMate();
}
public boolean isDraw() {
return board.isDraw();
}
public String getCurrentPlayer() {
return board.getSideToMove().toString(); // "WHITE" oder "BLACK"
}
private String initialFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
private static final Logger logger = Logger.getLogger(ChessEngine.class.getName());
private int quicksaveWhiteTimeLeft = -1;
private int quicksaveBlackTimeLeft = -1;
private int currentMoveIndex = 0;
private Timer whiteTimer;
private Timer blackTimer;
private final GameMode mode;
private String quicksaveFen = null;
private List<Move> quicksaveMoves = null;
private int quicksaveMoveIndex = 0;
private Opening detectedOpening = null;
public ChessEngine() {
this.mode = null;
logging();
board = new Board();
}
public ChessEngine(GameMode mode) {
this.mode = mode;
whiteTimer = new Timer(mode.minutes, mode.seconds);
blackTimer = new Timer(mode.minutes, mode.seconds);
logging();
board = new Board();
}
public boolean move(MoveDTO move) {
String from = "" + (char) ('A' + move.getFromCol()) + (8 - move.getFromRow());
String to = "" + (char) ('A' + move.getToCol()) + (8 - move.getToRow());
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
if (board.legalMoves().contains(libMove)) {
board.doMove(libMove);
if (currentMoveIndex < moves.size()) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Replay-Modus: Züge nach " + currentMoveIndex + " werden entfernt.");
}
moves = new ArrayList<>(moves.subList(0, currentMoveIndex));
}
moves.add(libMove);
currentMoveIndex++;
if (logger.isLoggable(Level.INFO)) {
logger.info("Zug erfolgreich durchgeführt: " + libMove);
}
String playedMovesUci = movesToUciString(moves);
detectedOpening = Opening.detect(playedMovesUci);
if (detectedOpening != null && logger.isLoggable(Level.INFO)) {
logger.info("Aktuelles Opening erkannt: " + detectedOpening.getEco() + " - " + detectedOpening.getName());
}
return true;
}
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Ungültiger Zug: " + libMove);
}
return false;
}
public String getOpeningName() {
if (detectedOpening != null) {
return detectedOpening.getEco() + " - " + detectedOpening.getName();
} else {
return "unbekannt";
}
}
private String movesToUciString(List<Move> moves) {
StringBuilder sb = new StringBuilder();
for (Move m : moves) {
sb.append(m.toString()).append(" ");
}
return sb.toString().trim();
}
public List<MoveDTO> getLegalDestinations(String from) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Hole legale Züge von: " + from);
}
List<MoveDTO> destinations = new ArrayList<>();
try {
Square fromSq = Square.valueOf(from.toUpperCase(java.util.Locale.ROOT));
for (Move move : board.legalMoves()) {
if (move.getFrom().equals(fromSq)) {
int fromRow = 8 - fromSq.getRank().ordinal() - 1;
int fromCol = fromSq.getFile().ordinal();
int toRow = 8 - move.getTo().getRank().ordinal() - 1;
int toCol = move.getTo().getFile().ordinal();
destinations.add(new MoveDTO(fromRow, fromCol, toRow, toCol));
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("Es wurden " + destinations.size() + " Ziele gefunden.");
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Fehler beim Holen der legalen Ziele: " + e.getMessage());
}
}
return destinations;
}
public List<String> getMoveListStringsGrouped() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Gruppiere Züge für Anzeige.");
}
List<String> result = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < moves.size(); i++) {
if (i % 2 == 0) {
sb.append((i / 2 + 1)).append(". ");
}
sb.append(moves.get(i).toString()).append(" ");
if (i % 2 == 1 || i == moves.size() - 1) {
result.add(sb.toString().trim());
sb = new StringBuilder();
}
}
return result;
}
public void quicksave() {
this.quicksaveFen = board.getFen();
this.quicksaveMoves = new ArrayList<>(moves);
this.quicksaveMoveIndex = currentMoveIndex;
if (whiteTimer != null) {
quicksaveWhiteTimeLeft = whiteTimer.getSecondsLeft();
}
if (blackTimer != null) {
quicksaveBlackTimeLeft = blackTimer.getSecondsLeft();
}
}
public boolean quickload() {
if (quicksaveFen == null) {
return false;
}
board = new Board();
board.loadFromFen(quicksaveFen);
moves = new ArrayList<>(quicksaveMoves);
currentMoveIndex = quicksaveMoveIndex;
if (whiteTimer != null && quicksaveWhiteTimeLeft != -1) {
whiteTimer.setSecondsLeft(quicksaveWhiteTimeLeft);
}
if (blackTimer != null && quicksaveBlackTimeLeft != -1) {
blackTimer.setSecondsLeft(quicksaveBlackTimeLeft);
}
return true;
}
public void clearQuicksave() {
quicksaveFen = null;
quicksaveMoves = null;
quicksaveMoveIndex = 0;
}
public PieceDTO getPieceAt(String square) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Hole Figur an Feld: " + square);
}
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase(java.util.Locale.ROOT)));
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());
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;
break;
}
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++;
if (logger.isLoggable(Level.INFO)) {
logger.info("Promotionszug durchgeführt: " + libMove);
}
return true;
}
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Ungültiger Promotionszug: " + libMove);
}
return false;
}
public BoardDTO getBoardAsDTO() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Erstelle DTO-Abbild des Boards");
}
PieceDTO[][] dtoBoard = new PieceDTO[8][8];
for (int rank = 8; rank >= 1; rank--) {
for (int file = 0; file < 8; file++) {
Square square = Square.valueOf("" + (char) ('A' + file) + rank);
Piece piece = board.getPiece(square);
dtoBoard[8 - rank][file] = convertPieceToDTO(piece);
}
}
return new BoardDTO(dtoBoard);
}
private String pieceToUnicode(Piece piece) {
switch (piece) {
case WHITE_KING:
return "♔";
case WHITE_QUEEN:
return "♕";
case WHITE_ROOK:
return "♖";
case WHITE_BISHOP:
return "♗";
case WHITE_KNIGHT:
return "♘";
case WHITE_PAWN:
return "♙";
case BLACK_KING:
return "♚";
case BLACK_QUEEN:
return "♛";
case BLACK_ROOK:
return "♜";
case BLACK_BISHOP:
return "♝";
case BLACK_KNIGHT:
return "♞";
case BLACK_PAWN:
return "♟";
default:
return " ";
}
}
public void setPositionToMoveIndex(int idx) {
if (logger.isLoggable(Level.INFO)) {
logger.info("Setze Board auf Zug-Index: " + idx);
}
board = new Board();
board.loadFromFen(initialFen);
for (int i = 0; i < idx; i++) {
board.doMove(moves.get(i));
}
currentMoveIndex = idx;
String playedMovesUci = movesToUciString(moves.subList(0, idx));
detectedOpening = Opening.detect(playedMovesUci);
}
public int getCurrentMoveIndex() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Hole aktuellen Zug-Index: " + currentMoveIndex);
}
return currentMoveIndex;
}
public int getMoveListSize() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Hole Anzahl gespielter Züge: " + moves.size());
}
return moves.size();
}
private PieceDTO convertPieceToDTO(Piece piece) {
if (piece == null || piece.equals(Piece.NONE)) {
return null;
}
String color = piece.name().startsWith("WHITE") ? "WHITE" : "BLACK";
String type = piece.name().substring(piece.name().indexOf('_') + 1);
String symbol = pieceToUnicode(piece);
return new PieceDTO(type, color, symbol);
}
public void setPositionFromFEN(String fen) {
board.loadFromFen(fen);
initialFen = fen;
}
public boolean isMated() {
boolean mated = board.isMated();
if (logger.isLoggable(Level.INFO)) {
logger.info("isMated() = " + mated);
}
return mated;
}
public boolean isStalemate() {
boolean stale = board.isStaleMate();
if (logger.isLoggable(Level.INFO)) {
logger.info("isStalemate() = " + stale);
}
return stale;
}
public boolean isDraw() {
boolean draw = board.isDraw();
if (logger.isLoggable(Level.INFO)) {
logger.info("isDraw() = " + draw);
}
return draw;
}
public String getCurrentPlayer() {
String player = board.getSideToMove().toString();
if (logger.isLoggable(Level.INFO)) {
logger.info("Am Zug: " + player);
}
return player;
}
public void logging() {
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
handler.setFormatter(new SimpleFormatter() {
@Override
public synchronized String format(LogRecord lr) {
return String.format("[%s] %s%n%n", lr.getLevel().getLocalizedName(), lr.getMessage());
}
});
logger.setUseParentHandlers(false);
logger.addHandler(handler);
if (logger.isLoggable(Level.INFO)) {
logger.info("ChessEngine wurde initialisiert.");
}
}
public List<Game> loadGamesFromPgn(String path) throws IOException {
PgnHolder pgnHolder = new PgnHolder(path);
try {
pgnHolder.loadPgn();
} catch (Exception e) {
e.printStackTrace();
}
return pgnHolder.getGames();
}
public void initTimers(int min, int sec) {
if (whiteTimer == null) {
whiteTimer = new Timer(min, sec);
} else if (quicksaveWhiteTimeLeft != -1) {
whiteTimer = new Timer(min, sec);
whiteTimer.setSecondsLeft(quicksaveWhiteTimeLeft);
} else {
whiteTimer = new Timer(min, sec);
}
if (blackTimer == null) {
blackTimer = new Timer(min, sec);
} else if (quicksaveBlackTimeLeft != -1) {
blackTimer = new Timer(min, sec);
blackTimer.setSecondsLeft(quicksaveBlackTimeLeft);
} else {
blackTimer = new Timer(min, sec);
}
}
public void saveAsPgn(Game game, String path, String dateiname) {
String event = safe(game.getRound().getEvent().getName());
String site = safe(game.getRound().getEvent().getSite());
String round = "" + game.getRound().getNumber();
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());
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\n");
StringBuilder movesSb = new StringBuilder();
String[] sanArray = game.getHalfMoves().toSanArray();
for (int i = 0; i < sanArray.length; i++) {
if (i % 2 == 0) {
movesSb.append((i / 2 + 1)).append(". ");
}
movesSb.append(sanArray[i]).append(" ");
}
movesSb.append(result);
String file = header + movesSb.toString();
try {
Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
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, List<Move> moves, int currentMoveIndex) {
Event event = new Event();
event.setName("Generated Game");
event.setSite("Local");
event.setStartDate(LocalDate.now().toString());
Round round = new Round(event);
round.setNumber(1);
Game game = new Game("1", round);
game.setWhitePlayer(new MyPlayer("White"));
game.setBlackPlayer(new MyPlayer("Black"));
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);
}
MoveList moveList = new MoveList();
for (Move move : moves) {
moveList.add(move);
}
game.setHalfMoves(moveList);
if (currentMoveIndex > 0 && currentMoveIndex <= moveList.size()) {
game.setPosition(currentMoveIndex - 1);
} else {
game.setPosition(moveList.size() - 1);
}
game.setBoard(new Board());
game.getBoard().loadFromFen(board.getFen());
return game;
}
public void undoLastMove() {
if (currentMoveIndex > 0 && !moves.isEmpty()) {
board.undoMove();
moves.remove(currentMoveIndex - 1);
currentMoveIndex--;
String playedMovesUci = movesToUciString(moves.subList(0, currentMoveIndex));
detectedOpening = Opening.detect(playedMovesUci);
}
}
public void loadMoves(List<Move> moveList) {
board = new Board();
moves.clear();
currentMoveIndex = 0;
for (Move move : moveList) {
board.doMove(move);
moves.add(move);
currentMoveIndex++;
}
}
public Timer getWhiteTimer() {
return whiteTimer;
}
public Timer getBlackTimer() {
return blackTimer;
}
public GameMode getGameMode() {
return mode;
}
}

View File

@ -0,0 +1,26 @@
package de.hs_mannheim.informatik.chess.model;
public enum GameMode {
CLASSIC(3, 0, 0),
RAPID(5, 0, 0),
BULLET(1, 0, 10); // 1 Minute + 10 Sek Inkrement
public final int minutes;
public final int seconds;
public final int incrementSeconds;
GameMode(int minutes, int seconds, int incrementSeconds) {
this.minutes = minutes;
this.seconds = seconds;
this.incrementSeconds = incrementSeconds;
}
@Override
public String toString() {
return switch (this) {
case CLASSIC -> "3 Minuten (klassisch)";
case RAPID -> "5 Minuten (rapid)";
case BULLET -> "1 Minute + 10 Sek Inkrement";
};
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,68 @@
package de.hs_mannheim.informatik.chess.model;
import java.util.*;
import java.nio.file.*;
import java.io.*;
public class Opening {
private final String eco;
private final String name;
private final String moves;
public Opening(String eco, String name, String moves) {
this.eco = eco;
this.name = name;
this.moves = moves;
}
public String getEco() { return eco; }
public String getName() { return name; }
public String getMoves() { return moves; }
// Öffnet und cached die Liste aus der gewünschten CSV
private static List<Opening> cachedOpenings = null;
public static List<Opening> allOpenings() {
if (cachedOpenings == null) {
cachedOpenings = new ArrayList<>();
// Passe den Pfad für deinen Benutzernamen an!
String path = "Ressources/all_openings.csv";
try (BufferedReader br = Files.newBufferedReader(Paths.get(path))) {
String line;
while ((line = br.readLine()) != null) {
// CSV-Format: ECO,"Name","Moves"
String[] parts = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
if (parts.length == 3) {
String eco = parts[0];
String name = parts[1].replaceAll("^\"|\"$", "");
String moves = parts[2].replaceAll("^\"|\"$", "");
cachedOpenings.add(new Opening(eco, name, moves));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
return cachedOpenings;
}
/**
* Sucht das längste passende Opening für eine gegebene Zugfolge (UCI).
* @param playedMovesUci UCI-Züge als String, z.B. "e2e4 e7e5 g1f3"
* @return bestes Opening oder null
*/
public static Opening detect(String playedMovesUci) {
Opening bestMatch = null;
int bestLength = 0;
for (Opening opening : allOpenings()) {
if (playedMovesUci.startsWith(opening.getMoves())) {
if (opening.getMoves().length() > bestLength) {
bestMatch = opening;
bestLength = opening.getMoves().length();
}
}
}
return bestMatch;
}
}

View File

@ -0,0 +1,68 @@
package de.hs_mannheim.informatik.chess.model;
import java.util.function.Consumer;
public class Timer {
private int secondsLeft;
private javax.swing.Timer swingTimer;
private Runnable onTimeout;
private Consumer<Integer> onTick;
public Timer(int minutes, int seconds) {
this.secondsLeft = minutes * 60 + seconds;
}
public void setOnTimeout(Runnable onTimeout) {
this.onTimeout = onTimeout;
}
public void setOnTick(Consumer<Integer> onTick) {
this.onTick = onTick;
}
public void start() {
if (swingTimer != null && swingTimer.isRunning()) {
swingTimer.stop();
}
swingTimer = new javax.swing.Timer(1000, e -> {
secondsLeft--;
if (onTick != null) {
onTick.accept(secondsLeft);
}
if (secondsLeft <= 0) {
stop();
if (onTimeout != null) {
onTimeout.run();
}
}
});
swingTimer.start();
}
public void stop() {
if (swingTimer != null) {
swingTimer.stop();
}
}
public void reset(int minutes, int seconds) {
stop();
this.secondsLeft = minutes * 60 + seconds;
if (onTick != null) {
onTick.accept(secondsLeft);
}
}
public int getSecondsLeft() {
return secondsLeft;
}
public void setSecondsLeft(int secondsLeft) {
this.secondsLeft = secondsLeft;
}
public void addSeconds(int seconds) {
this.secondsLeft += seconds;
}
}

View File

@ -0,0 +1,254 @@
package de.hs_mannheim.informatik.chess.view;
import javax.swing.*;
import java.awt.*;
import java.util.HashMap;
public class CreativeGui {
public interface StartGameCallback {
void onStartGame(String fen);
}
private boolean isFlipped = false;
private JFrame frame;
private JLabel[][] fields = new JLabel[8][8];
private String selectedPiece = null;
private JTextField fenField;
private JButton updateBtn;
private JButton flipBoardButton;
private boolean closedByUser = true;
private StartGameCallback startGameCallback;
private JButton startGameButton;
public static final HashMap<String, String> UNICODE_MAP = new HashMap<String, String>() {{
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");
}};
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.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(false);
frame.setLocationRelativeTo(null);
JPanel mainPanel = new JPanel(new GridBagLayout());
mainPanel.setBackground(LIGHT);
GridBagConstraints gbc = new GridBagConstraints();
// LINKS: chessPanel (Board+Toolbars)
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0.7;
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.3;
gbc.weighty = 1.0;
gbc.insets = new Insets(5, 0, 5, 5);
gbc.fill = GridBagConstraints.BOTH;
mainPanel.add(fenPanel(), gbc);
frame.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
if (closedByUser) {
new de.hs_mannheim.informatik.chess.controller.MainController();
}
}
});
frame.setContentPane(mainPanel);
frame.setVisible(true);
}
private JPanel toolbarPanel(boolean white) {
JPanel panel = new JPanel();
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, 40));
btn.setFocusPainted(false);
btn.setPreferredSize(new Dimension(70, 70));
btn.setBackground(white ? LIGHT : DARK);
btn.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
String key = prefix + type;
btn.addActionListener(e -> selectedPiece = key);
panel.add(btn);
}
JButton del = new JButton(UNICODE_MAP.get("ERASER"));
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(del);
return panel;
}
private 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));
label.setBackground((row + col) % 2 == 0 ? LIGHT : DARK);
fields[row][col] = label;
boardPanel.add(label);
}
}
return boardPanel;
}
private JPanel chessPanel() {
JPanel chessPanel = new JPanel(new BorderLayout());
chessPanel.setBackground(new Color(0x1b263b));
// Board UND Toolbars in EINEM Panel, damit sie linksbündig mit dem Brett starten!
JPanel boardAndToolbars = new JPanel(new BorderLayout());
boardAndToolbars.setOpaque(false);
// Toolbar Schwarz (oben)
JPanel blackToolbar = toolbarPanel(false);
boardAndToolbars.add(blackToolbar, BorderLayout.NORTH);
// Board (zentriert im Panel)
JPanel board = boardPanel();
boardAndToolbars.add(board, BorderLayout.CENTER);
// Toolbar Weiß (unten)
JPanel whiteToolbar = toolbarPanel(true);
boardAndToolbars.add(whiteToolbar, BorderLayout.SOUTH);
// Board+Toolbars mittig in einem Panel mit FlowLayout, damit sie zentriert sind
JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
centerPanel.setOpaque(false);
centerPanel.add(boardAndToolbars);
blackToolbar.setPreferredSize(new Dimension(0, 70)); // 70 px hoch
whiteToolbar.setPreferredSize(new Dimension(0, 70));
// Dynamisch skalieren wie gehabt
centerPanel.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent evt) {
int totalHeight = centerPanel.getHeight();
int totalWidth = centerPanel.getWidth();
int toolbarHeight = blackToolbar.getPreferredSize().height + whiteToolbar.getPreferredSize().height;
// Falls Toolbars keine PrefSize haben, nimm getHeight()
if (toolbarHeight == 0) toolbarHeight = blackToolbar.getHeight() + whiteToolbar.getHeight();
int boardMaxHeight = totalHeight - toolbarHeight;
int size = Math.min(totalWidth, boardMaxHeight);
board.setPreferredSize(new Dimension(size, size));
board.revalidate();
}
});
chessPanel.add(centerPanel, BorderLayout.CENTER);
// Drehknopf rechts, schön mittig
JPanel eastPanel = new JPanel();
eastPanel.setOpaque(false);
eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.Y_AXIS));
eastPanel.add(Box.createVerticalGlue());
eastPanel.add(flipBoardButton = createFlipButton());
eastPanel.add(Box.createVerticalGlue());
chessPanel.add(eastPanel, BorderLayout.EAST);
// Buffer links, wenn du willst
chessPanel.add(Box.createRigidArea(new Dimension(40, 0)), BorderLayout.WEST);
return chessPanel;
}
// Flip-Knopf Builder, damit dus an mehreren Stellen nutzen kannst:
private JButton createFlipButton() {
JButton btn = new JButton("⇵");
btn.setPreferredSize(new Dimension(70, 70));
btn.setFont(new Font("SansSerif", Font.BOLD, 40));
btn.setBackground(new Color(0x5500ff));
btn.setForeground(Color.WHITE);
btn.setFocusPainted(false);
return btn;
}
private JPanel fenPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(new Color(0xe0e1dd));
panel.setPreferredSize(new Dimension(420, 40));
panel.add(new JLabel("FEN:"));
fenField = new JTextField("");
fenField.setFont(new Font("Monospaced", Font.PLAIN, 22));
fenField.setMaximumSize(new Dimension(600, 50));
panel.add(fenField);
updateBtn = new JButton("Update Board");
updateBtn.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(updateBtn);
startGameButton = new JButton("🟢 Partie starten");
startGameButton.setAlignmentX(Component.CENTER_ALIGNMENT);
startGameButton.setBackground(new Color(0x218838));
startGameButton.setForeground(Color.WHITE);
panel.add(startGameButton);
startGameButton.addActionListener(e -> {
if (startGameCallback != null) {
setClosedByUser(false);
startGameCallback.onStartGame(getFenText());
}
});
return panel;
}
// 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; }
public void setClosedByUser(boolean b) {
this.closedByUser = b;
}
public JButton getStartGameButton() { return startGameButton; }
public void setStartGameCallback(StartGameCallback callback) {
this.startGameCallback = callback;
}
public boolean isFlipped() { return isFlipped; }
public void setFlipped(boolean f) { isFlipped = f; }
public JButton getFlipBoardButton() { return flipBoardButton; }
public void close() {
frame.dispose();
}
}

View File

@ -7,9 +7,12 @@ import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.util.List;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
@ -23,10 +26,28 @@ import de.hs_mannheim.informatik.chess.model.BoardDTO;
import de.hs_mannheim.informatik.chess.model.PieceDTO;
public class GameGui {
JFrame frame;
private JLabel[][] fields = new JLabel[8][8];
private JButton flipBoardButton;
private boolean isFlipped = false;
JButton btnSave = new JButton("💾");
private JLabel whiteTimerLabel;
private JLabel blackTimerLabel;
private JLabel openingLabel;
private JButton btnFirst = new JButton("|<");
private JButton btnPrev = new JButton("<");
private JButton btnNext = new JButton(">");
private JButton btnLast = new JButton(">|");
private JButton resignButton;
private JButton drawButton;
private JButton undoButton;
Color LIGHT = new Color(0xe0e1dd);
Color DARK = new Color(0x778da9);
private JPanel moveListPanel;
private JScrollPane moveListScroll;
@ -38,31 +59,34 @@ public class GameGui {
public JFrame mainFrame() {
JFrame frame = new JFrame();
frame.setSize(1600, 1000);
frame = new JFrame();
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(false);
frame.setLocationRelativeTo(null);
frame.add(mainPanel());
frame.setDefaultCloseOperation(2);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
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));
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());
@ -92,68 +116,113 @@ 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; // <-- Save the 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;
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 boardPanel) {
JPanel chessPanel = new JPanel(new BorderLayout());
chessPanel.setBackground(new Color(0x1b263b));
// --- Eröffnungslabel oben ---
openingLabel = new JLabel("Eröffnung: unbekannt", SwingConstants.CENTER);
openingLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
openingLabel.setForeground(Color.WHITE);
openingLabel.setOpaque(true);
openingLabel.setBackground(new Color(0x283655));
openingLabel.setPreferredSize(new Dimension(800, 50));
chessPanel.add(openingLabel, BorderLayout.NORTH);
// --- Board in ein zentriertes Panel mit fixer Größe ---
JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
centerPanel.setOpaque(false);
centerPanel.add(boardPanel);
centerPanel.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent evt) {
int size = Math.min(centerPanel.getWidth(), centerPanel.getHeight());
boardPanel.setPreferredSize(new Dimension(size, size));
boardPanel.revalidate();
}
});
chessPanel.add(centerPanel, BorderLayout.CENTER);
// --- Dummy-Buffer für WEST und EAST ---
chessPanel.add(Box.createRigidArea(new Dimension(40, 0)), BorderLayout.WEST);
chessPanel.add(Box.createRigidArea(new Dimension(40, 0)), BorderLayout.EAST);
// --- Buttonleiste unten bauen ---
JPanel buttonRow = new JPanel();
buttonRow.setOpaque(false);
buttonRow.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT, 10, 0));
resignButton = new JButton("🏳");
resignButton.setPreferredSize(new Dimension(70, 70));
resignButton.setFont(new Font("SansSerif", Font.BOLD, 35));
resignButton.setBackground(new Color(0xff0044));
resignButton.setForeground(Color.WHITE);
resignButton.setFocusPainted(false);
drawButton = new JButton("½");
drawButton.setPreferredSize(new Dimension(70, 70));
drawButton.setFont(new Font("SansSerif", Font.BOLD, 35));
drawButton.setBackground(new Color(0x0080ff));
drawButton.setForeground(Color.WHITE);
drawButton.setFocusPainted(false);
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);
undoButton = new JButton("↧");
undoButton.setPreferredSize(new Dimension(70, 70));
undoButton.setFont(new Font("SansSerif", Font.BOLD, 40));
undoButton.setBackground(new Color(0xffa500)); // Orange
undoButton.setForeground(Color.WHITE);
undoButton.setFocusPainted(false);
buttonRow.add(undoButton);
buttonRow.add(resignButton);
buttonRow.add(drawButton);
buttonRow.add(flipBoardButton);
chessPanel.add(buttonRow, BorderLayout.SOUTH);
return chessPanel;
}
public JPanel statsPanel() {
JPanel statsPanel = new JPanel(new BorderLayout());
statsPanel.setBackground(new Color(0x0d1b2a));
// Move-Liste (scrollbar, wie gehabt)
// Panel für die Timer (NORD)
statsPanel.add(timerPanel(), BorderLayout.NORTH);
// Move-Liste
moveListPanel = new JPanel();
moveListPanel.setLayout(new BoxLayout(moveListPanel, BoxLayout.Y_AXIS));
moveListPanel.setBackground(new Color(0x0d1b2a));
@ -161,33 +230,68 @@ 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 je nach Geschmack
buttonPanel.setLayout(new GridLayout(1, 4, 10, 0));
JButton btnFirst = new JButton("|<");
JButton btnPrev = new JButton("<");
JButton btnNext = new JButton(">");
JButton btnLast = new JButton(">|");
// Style (optional)
buttonPanel.setLayout(new GridLayout(1, 5, 10, 0)); // Jetzt 5 statt 4 Spalten!
btnFirst.setBackground(new Color(0x212529)); btnFirst.setForeground(Color.WHITE);
btnPrev.setBackground(new Color(0x212529)); btnPrev.setForeground(Color.WHITE);
btnNext.setBackground(new Color(0x212529)); btnNext.setForeground(Color.WHITE);
btnLast.setBackground(new Color(0x212529)); btnLast.setForeground(Color.WHITE);
// Hinzufügen
btnSave.setBackground(new Color(0x218838)); btnSave.setForeground(Color.WHITE);
buttonPanel.add(btnFirst);
buttonPanel.add(btnPrev);
buttonPanel.add(btnNext);
buttonPanel.add(btnLast);
// Unten ins BorderLayout
buttonPanel.add(btnSave);
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ß: --:--", SwingConstants.CENTER);
blackTimerLabel = new JLabel("Schwarz: --:--", 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"};
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<String> moves) {
moveListPanel.removeAll();
@ -217,6 +321,14 @@ public class GameGui {
this.isFlipped = flipped;
}
public JButton getBtnFirst() { return btnFirst; }
public JButton getBtnPrev() { return btnPrev; }
public JButton getBtnNext() { return btnNext; }
public JButton getBtnLast() { return btnLast; }
public JButton getBtnSave() { return btnSave; }
public JButton getResignButton() { return resignButton; }
public JButton getDrawButton() { return drawButton; }
public void updateBoard(BoardDTO boardDTO) {
PieceDTO[][] board = boardDTO.getBoard();
if (!isFlipped) {
@ -237,9 +349,38 @@ 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 setOpeningLabel(String text) {
openingLabel.setText("Eröffnung: " + text);
}
public JButton getUndoButton() {
return undoButton;
}
public void displayMessage(String msg) {
JOptionPane.showMessageDialog(null, msg);
}
public JFrame getFrame() {
return frame;
}
}

View File

@ -0,0 +1,23 @@
package de.hs_mannheim.informatik.chess.view;
import javax.swing.*;
import de.hs_mannheim.informatik.chess.model.GameMode;
public class GameModeSelector {
public static GameMode selectGameMode() {
GameMode[] options = GameMode.values();
GameMode selected = (GameMode) JOptionPane.showInputDialog(
null,
"Wähle den Spielmodus:",
"Spielmodus auswählen",
JOptionPane.QUESTION_MESSAGE,
null,
options,
GameMode.CLASSIC
);
return selected != null ? selected : GameMode.CLASSIC;
}
}

View File

@ -1,19 +1,24 @@
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);
frame.setSize(1600, 1000);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(false);
frame.setLocationRelativeTo(null);
//Haupt-Panel mit GridBagLayout
@ -38,34 +43,38 @@ 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)");
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 = 2;
mainPanel.add(btnCreative, gbc);
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));

View File

@ -0,0 +1,309 @@
package de.hs_mannheim.informatik.chess.view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
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.Box;
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(">|");
private JLabel openingLabel;
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.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(false);
frame.setLocationRelativeTo(null);
frame.add(mainPanel());
frame.setDefaultCloseOperation(2);
frame.setVisible(true);
frame.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosed(java.awt.event.WindowEvent e) {
new de.hs_mannheim.informatik.chess.controller.MainController();
}
});
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 boardPanel) {
JPanel chessPanel = new JPanel(new BorderLayout());
chessPanel.setBackground(new Color(0x1b263b));
// --- Eröffnungslabel oben ---
openingLabel = new JLabel("Eröffnung: unbekannt", SwingConstants.CENTER);
openingLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
openingLabel.setForeground(Color.WHITE);
openingLabel.setOpaque(true);
openingLabel.setBackground(new Color(0x283655));
openingLabel.setPreferredSize(new Dimension(800, 50));
chessPanel.add(openingLabel, BorderLayout.NORTH);
// --- Board in ein zentriertes Panel mit fixer Größe ---
JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
centerPanel.setOpaque(false);
centerPanel.add(boardPanel);
centerPanel.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentResized(java.awt.event.ComponentEvent evt) {
int size = Math.min(centerPanel.getWidth(), centerPanel.getHeight());
boardPanel.setPreferredSize(new Dimension(size, size));
boardPanel.revalidate();
}
});
chessPanel.add(centerPanel, BorderLayout.CENTER);
// --- Dummy-Buffer für WEST und EAST ---
chessPanel.add(Box.createRigidArea(new Dimension(40, 0)), BorderLayout.WEST);
chessPanel.add(Box.createRigidArea(new Dimension(40, 0)), BorderLayout.EAST);
// --- Buttonleiste unten bauen ---
JPanel buttonRow = new JPanel();
buttonRow.setOpaque(false);
buttonRow.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.RIGHT, 10, 0));
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);
buttonRow.add(flipBoardButton);
chessPanel.add(buttonRow, BorderLayout.SOUTH);
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<String> 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 void setOpeningLabel(String text) {
openingLabel.setText("Eröffnung: " + text);
}
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);
}
}

View File

@ -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<Game> games, Consumer<Game> 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);
}
}

View File

@ -1,36 +0,0 @@
package de.hs_mannheim.informatik.chess.test;
import junit.framework.*;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static TestSuite suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}

View File

@ -0,0 +1,222 @@
package de.hs_mannheim.informatik.chess.test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import de.hs_mannheim.informatik.chess.model.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ModelTest {
private ChessEngine engine;
@BeforeEach
public void setUp() {
engine = new ChessEngine(); // Echte Instanz!
}
@Test
public void ErkenntObZugLegalOderIllegal()
{
assertTrue(engine.move(new MoveDTO(6, 4, 4, 4)));
assertFalse(engine.move(new MoveDTO(7, 0, 5, 1)));
}
@Test
public void GibtLegaleMovesAn() {
List<MoveDTO> mvs = new ArrayList<>();
mvs.add(new MoveDTO(6,4,5,4));
mvs.add(new MoveDTO(6,4,4,4));
List<MoveDTO> moves = engine.getLegalDestinations("E2");
assertEquals(2, moves.size());
assertTrue(moves.stream().anyMatch(m -> m.getToRow() == 5 && m.getToCol() == 4));
assertTrue(moves.stream().anyMatch(m -> m.getToRow() == 4 && m.getToCol() == 4));
}
@Test //Stichprobenartige Kontrolle ob richtige Figur an richtiger Stelle
public void BoardWirdRichtigAufgebaut() {
BoardDTO board = engine.getBoardAsDTO();
PieceDTO a1 = board.getBoard()[7][0];
assertEquals("ROOK", a1.getType());
assertEquals("WHITE", a1.getColor());
PieceDTO e1 = board.getBoard()[7][4];
assertEquals("KING", e1.getType());
assertEquals("WHITE", e1.getColor());
PieceDTO a8 = board.getBoard()[0][0];
assertEquals("ROOK", a8.getType());
assertEquals("BLACK", a8.getColor());
PieceDTO e8 = board.getBoard()[0][4];
assertEquals("KING", e8.getType());
assertEquals("BLACK", e8.getColor());
PieceDTO e2 = board.getBoard()[6][4];
assertEquals("PAWN", e2.getType());
assertEquals("WHITE", e2.getColor());
PieceDTO d7 = board.getBoard()[1][3];
assertEquals("PAWN", d7.getType());
assertEquals("BLACK", d7.getColor());
PieceDTO e4 = board.getBoard()[4][4];
assertNull(e4);
PieceDTO d5 = board.getBoard()[3][3];
assertNull(d5);
}
@Test
public void speichertMovesRichtigGruppiert() {
engine.move(new MoveDTO(6, 6, 4, 6));
engine.move(new MoveDTO(1, 4, 3, 4));
engine.move(new MoveDTO(7, 6, 5, 5)); //Züge um das speichern zu testen
engine.move(new MoveDTO(0, 1, 2, 2));
engine.move(new MoveDTO(7, 1, 5, 2));
List<String> gruppiertGespeichert = engine.getMoveListStringsGrouped(); //Moves in Liste gespeichert um zu testen
assertTrue(gruppiertGespeichert.size() >= 3, "Zu wenige gespeicherte Gruppen");
assertTrue(gruppiertGespeichert.get(0).startsWith("1."));
assertTrue(gruppiertGespeichert.get(1).startsWith("2."));
assertTrue(gruppiertGespeichert.get(2).startsWith("3."));
for (int i = 0; i < gruppiertGespeichert.size(); i++) {
String[] parts = gruppiertGespeichert.get(i).split(" ");
if (i == gruppiertGespeichert.size() - 1) {
assertTrue(parts.length == 2 || parts.length == 3);
} else {
assertEquals(3, parts.length);
}
}
}
@Test
public void erkenntMattKorrekt() {
assertFalse(engine.isMated()); //<--Test, dass nicht immer alles einfach Matt ist
engine.setPositionFromFEN("7k/6Q1/5K2/8/8/8/8/8 b - - 0 1"); //<--Matt position vorbereiten
System.out.println("Aktueller Spieler: " + engine.getCurrentPlayer());
System.out.println("isMated(): " + engine.isMated());
assertEquals("BLACK", engine.getCurrentPlayer());
assertTrue(engine.isMated()); //<Test ob nun spieler Matt ist
}
@Test
public void erkenntPattKorrekt() {
assertFalse(engine.isStalemate());
engine.setPositionFromFEN("7k/5Q2/6K1/8/8/8/8/8 b - - 0 1");
System.out.println("Aktueller Spieler: " + engine.getCurrentPlayer());
System.out.println("isStalemate(): " + engine.isStalemate());
assertEquals("BLACK", engine.getCurrentPlayer());
assertTrue(engine.isStalemate());
}
@Test
public void erkenntUnentschieden() {
assertFalse(engine.isDraw());
engine.setPositionFromFEN("8/8/8/8/8/8/8/4K2k w - - 0 1"); // nur zwei Könige
assertTrue(engine.isDraw());
}
@Test
public void MethodeGibtRichtigeFigurZurück() {
String type = "KING";
String color= "WHITE";
PieceDTO test = engine.getPieceAt("E1");
assertEquals(type, test.getType());
assertEquals(color, test.getColor());
}
@Test
public void MethodeGibtRichtigenPlayerZurück() {
assertEquals("WHITE", engine.getCurrentPlayer());
engine.move(new MoveDTO(6, 4, 4, 4));
assertEquals("BLACK", engine.getCurrentPlayer());
}
@Test
public void MethodeSetztBoardRichtigzurück() {
engine.move(new MoveDTO(6,4,4,4)); // e2-e4
engine.move(new MoveDTO(1,4,3,4)); // e7-e5
engine.setPositionToMoveIndex(1);
PieceDTO pawn = engine.getPieceAt("E4");
assertNotNull(pawn);
assertEquals("PAWN", pawn.getType());
assertNull(engine.getPieceAt("E5"));
assertEquals(1, engine.getCurrentMoveIndex());
}
@Test
public void FigurZurQueenBefördernklappt() {
engine.setPositionFromFEN("8/P7/8/8/8/8/8/k6K w - - 0 1"); // Weißer Bauer auf a7, Weiß am Zug
boolean moved = engine.moveWithPromotion(new MoveDTO(1, 0, 0, 0), "QUEEN"); // a7-a8=Dame
assertTrue(moved);
PieceDTO piece = engine.getPieceAt("A8");
assertEquals("QUEEN", piece.getType());
assertEquals("WHITE", piece.getColor());
}
@Test
public void fenAufbauFunktioniert() {
// Beispiel-FEN: Weißer König auf e1, schwarze Dame auf d8, schwarzer Bauer auf a7, Rest leer
String fen = "3q4/p7/8/8/8/8/8/4K3 w - - 0 1";
engine.setPositionFromFEN(fen);
// Kontrolliere schwarze Dame auf d8 (0,3)
PieceDTO d8 = engine.getPieceAt("D8");
assertEquals("QUEEN", d8.getType());
assertEquals("BLACK", d8.getColor());
// Kontrolliere schwarzen Bauern auf a7 (1,0)
PieceDTO a7 = engine.getPieceAt("A7");
assertEquals("PAWN", a7.getType());
assertEquals("BLACK", a7.getColor());
// Kontrolliere weißen König auf e1 (7,4)
PieceDTO e1 = engine.getPieceAt("E1");
assertEquals("KING", e1.getType());
assertEquals("WHITE", e1.getColor());
// Leeres Feld testen: e2 (6,4)
PieceDTO e2 = engine.getPieceAt("E2");
assertNull(e2);
// Leeres Feld testen: h5 (3,7)
PieceDTO h5 = engine.getPieceAt("H5");
assertNull(h5);
}
@Test
public void timerWirdRichtigErstellt() {
engine.initTimers(5, 30);
assertEquals(330, engine.getWhiteTimer().getSecondsLeft());
assertEquals(330, engine.getBlackTimer().getSecondsLeft());
}
}