Compare commits
No commits in common. "Main" and "Controller" have entirely different histories.
Main
...
Controller
|
|
@ -1,259 +0,0 @@
|
||||||
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 (A–H) 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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
|
|
@ -10,12 +10,13 @@
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>schach</name>
|
<name>schach</name>
|
||||||
<description>A simple schach project</description>
|
<description>A simple schach.</description>
|
||||||
<url>http://www.example.com</url>
|
<url>http://www.example.com</url>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.release>17</maven.compiler.release>
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
@ -26,31 +27,22 @@
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Schachlib -->
|
<!-- Schachlib (bhlangonijr) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.bhlangonijr</groupId>
|
<groupId>com.github.bhlangonijr</groupId>
|
||||||
<artifactId>chesslib</artifactId>
|
<artifactId>chesslib</artifactId>
|
||||||
<version>1.3.4</version>
|
<version>1.3.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- JUnit 3 (wie im Template, ggf. auf JUnit 4/5 upgraden für moderne Projekte) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>5.10.2</version>
|
<version>3.8.1</version>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Mockito -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-core</artifactId>
|
|
||||||
<version>5.11.0</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
||||||
<pluginManagement>
|
<pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -91,58 +83,12 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</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>
|
</build>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
<version>3.6.1</version>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
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.gui.Gui;
|
||||||
|
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
||||||
|
|
||||||
|
public class Controller {
|
||||||
|
Gui gui;
|
||||||
|
ChessEngine engine;
|
||||||
|
private int selectedRow = -1, selectedCol = -1;
|
||||||
|
private List<int[]> highlightedFields = new ArrayList<>();
|
||||||
|
|
||||||
|
public Controller(Gui gui, ChessEngine engine) {
|
||||||
|
this.gui = gui;
|
||||||
|
this.engine = engine;
|
||||||
|
initListeners();
|
||||||
|
updateGuiBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initListeners() {
|
||||||
|
for (int row = 0; row < 8; row++) {
|
||||||
|
for (int col = 0; col < 8; col++) {
|
||||||
|
final int r = row, c = col;
|
||||||
|
gui.getField(row, col).addMouseListener(new MouseAdapter() {
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
handleClick(r, c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleClick(int row, int col) {
|
||||||
|
if (selectedRow == -1 && selectedCol == -1) {
|
||||||
|
// Erstes Feld gewählt
|
||||||
|
selectedRow = row;
|
||||||
|
selectedCol = col;
|
||||||
|
gui.getField(row, col).setBorder(BorderFactory.createLineBorder(Color.RED, 2));
|
||||||
|
|
||||||
|
//Mögliche Ziele highlighten
|
||||||
|
String from = coordToChessNotation(row, col);
|
||||||
|
List<String> targets = engine.getLegalDestinations(from);
|
||||||
|
for (String t : targets) {
|
||||||
|
int[] xy = chessNotationToCoord(t);
|
||||||
|
gui.getField(xy[0], xy[1]).setBackground(Color.YELLOW);
|
||||||
|
highlightedFields.add(xy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Zweites Feld: Zug probieren
|
||||||
|
// Highlight entfernen
|
||||||
|
for (int[] xy : highlightedFields) {
|
||||||
|
resetFieldBackground(xy[0], xy[1]);
|
||||||
|
}
|
||||||
|
highlightedFields.clear();
|
||||||
|
|
||||||
|
gui.getField(selectedRow, selectedCol).setBorder(null);
|
||||||
|
handleMove(selectedRow, selectedCol, row, col);
|
||||||
|
selectedRow = -1;
|
||||||
|
selectedCol = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleMove(int fromX, int fromY, int toX, int toY) {
|
||||||
|
String from = coordToChessNotation(fromX, fromY);
|
||||||
|
String to = coordToChessNotation(toX, toY);
|
||||||
|
if (engine.move(from, to)) {
|
||||||
|
updateGuiBoard();
|
||||||
|
|
||||||
|
// Jetzt: Spielstatus prüfen!
|
||||||
|
if (engine.isMated()) {
|
||||||
|
String winner = engine.getCurrentPlayer().equals("WHITE") ? "SCHWARZ" : "WEIß";
|
||||||
|
gui.displayMessage(winner + " hat gewonnen (Schachmatt)!");
|
||||||
|
// Optional: Hier Spiel beenden/disable oder restart anbieten
|
||||||
|
} else if (engine.isStalemate() || engine.isDraw()) {
|
||||||
|
gui.displayMessage("Remis! (Stalemate oder andere Regel)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gui.displayMessage("Ungültiger Zug!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateGuiBoard() {
|
||||||
|
gui.updateBoard(engine.getBoardUnicode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String coordToChessNotation(int x, int y) {
|
||||||
|
// (0,0) -> "A8", (7,7) -> "H1"
|
||||||
|
char file = (char)('A' + y); // ACHTUNG! col == y == FILE, row == x == RANK
|
||||||
|
int rank = 8 - x;
|
||||||
|
return "" + file + rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] chessNotationToCoord(String square) {
|
||||||
|
// "A8" -> (0,0), "H1" -> (7,7)
|
||||||
|
char file = square.charAt(0);
|
||||||
|
int rank = square.charAt(1) - '0';
|
||||||
|
int x = 8 - rank;
|
||||||
|
int y = file - 'A';
|
||||||
|
return new int[] {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,169 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,438 +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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.controller;
|
|
||||||
|
|
||||||
public interface GameEndCallback {
|
|
||||||
void onNewGameRequested();
|
|
||||||
void onReturnToMenu();
|
|
||||||
}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
package de.hs_mannheim.informatik.chess.gui;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.Insets;
|
||||||
|
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
|
public class Gui {
|
||||||
|
|
||||||
|
private JLabel[][] fields = new JLabel[8][8];
|
||||||
|
|
||||||
|
public Gui(){
|
||||||
|
mainFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JFrame mainFrame() {
|
||||||
|
JFrame frame = new JFrame();
|
||||||
|
frame.setSize(1600, 1000);
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.add(mainPanel());
|
||||||
|
|
||||||
|
frame.setDefaultCloseOperation(2);
|
||||||
|
frame.setVisible(true);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 40));
|
||||||
|
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);
|
||||||
|
return chessPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel statsPanel() {
|
||||||
|
JPanel statsPanel = new JPanel();
|
||||||
|
statsPanel.setBackground(new Color(0x0d1b2a));
|
||||||
|
return statsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabel getField(int row, int col) {
|
||||||
|
return fields[row][col];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBoard(String[][] unicodeBoard) {
|
||||||
|
for (int row = 0; row < 8; row++) {
|
||||||
|
for (int col = 0; col < 8; col++) {
|
||||||
|
fields[row][col].setText(unicodeBoard[row][col]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayMessage(String msg) {
|
||||||
|
JOptionPane.showMessageDialog(null, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package de.hs_mannheim.informatik.chess.main;
|
package de.hs_mannheim.informatik.chess.main;
|
||||||
import de.hs_mannheim.informatik.chess.controller.MainController;
|
import de.hs_mannheim.informatik.chess.gui.Gui;
|
||||||
|
import de.hs_mannheim.informatik.chess.controller.Controller;
|
||||||
|
import de.hs_mannheim.informatik.chess.model.ChessEngine;
|
||||||
|
|
||||||
|
|
||||||
public class Main{
|
public class Main{
|
||||||
|
|
||||||
public static void main( String[] args ){
|
public static void main( String[] args ){
|
||||||
new MainController();
|
Gui gui = new Gui();
|
||||||
|
ChessEngine engine = new ChessEngine();
|
||||||
|
new Controller(gui, engine);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
|
||||||
|
|
||||||
public class BoardDTO {
|
|
||||||
|
|
||||||
private PieceDTO[][] board; // 8x8 Feld
|
|
||||||
|
|
||||||
public BoardDTO(PieceDTO[][] board) {
|
|
||||||
this.board = board;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PieceDTO[][] getBoard() {
|
|
||||||
return board;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBoard(PieceDTO[][] board) {
|
|
||||||
this.board = board;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,524 +1,90 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
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.ArrayList;
|
||||||
import java.util.List;
|
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.Board;
|
||||||
import com.github.bhlangonijr.chesslib.Piece;
|
import com.github.bhlangonijr.chesslib.Piece;
|
||||||
import com.github.bhlangonijr.chesslib.Square;
|
import com.github.bhlangonijr.chesslib.Square;
|
||||||
import com.github.bhlangonijr.chesslib.move.Move;
|
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 {
|
public class ChessEngine {
|
||||||
private Board board;
|
private Board board;
|
||||||
private List<Move> moves = new ArrayList<>();
|
|
||||||
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() {
|
public ChessEngine() {
|
||||||
this.mode = null;
|
board = new Board();
|
||||||
logging();
|
}
|
||||||
board = new Board();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChessEngine(GameMode mode) {
|
public boolean move(String from, String to) {
|
||||||
this.mode = mode;
|
Move move = new Move(Square.valueOf(from.toUpperCase()), Square.valueOf(to.toUpperCase()));
|
||||||
whiteTimer = new Timer(mode.minutes, mode.seconds);
|
if (board.legalMoves().contains(move)) {
|
||||||
blackTimer = new Timer(mode.minutes, mode.seconds);
|
board.doMove(move);
|
||||||
logging();
|
return true;
|
||||||
board = new Board();
|
} else {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean move(MoveDTO move) {
|
public List<String> getLegalDestinations(String from) {
|
||||||
String from = "" + (char) ('A' + move.getFromCol()) + (8 - move.getFromRow());
|
List<String> destinations = new ArrayList<>();
|
||||||
String to = "" + (char) ('A' + move.getToCol()) + (8 - move.getToRow());
|
Square fromSq = Square.valueOf(from.toUpperCase());
|
||||||
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
|
for (Move move : board.legalMoves()) {
|
||||||
if (board.legalMoves().contains(libMove)) {
|
if (move.getFrom().equals(fromSq)) {
|
||||||
board.doMove(libMove);
|
destinations.add(move.getTo().toString()); // z.B. "E4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentMoveIndex < moves.size()) {
|
public String getPieceAt(String square) {
|
||||||
if (logger.isLoggable(Level.INFO)) {
|
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase()));
|
||||||
logger.info("Replay-Modus: Züge nach " + currentMoveIndex + " werden entfernt.");
|
return piece.toString(); // z.B. "WHITE_PAWN"
|
||||||
}
|
}
|
||||||
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);
|
public String[][] getBoardUnicode() {
|
||||||
detectedOpening = Opening.detect(playedMovesUci);
|
String[][] unicodeBoard = new String[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);
|
||||||
|
unicodeBoard[8-rank][file] = pieceToUnicode(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unicodeBoard;
|
||||||
|
}
|
||||||
|
|
||||||
if (detectedOpening != null && logger.isLoggable(Level.INFO)) {
|
private String pieceToUnicode(Piece piece) {
|
||||||
logger.info("Aktuelles Opening erkannt: " + detectedOpening.getEco() + " - " + detectedOpening.getName());
|
switch (piece) {
|
||||||
}
|
case WHITE_KING: return "♔";
|
||||||
return true;
|
case WHITE_QUEEN: return "♕";
|
||||||
}
|
case WHITE_ROOK: return "♖";
|
||||||
if (logger.isLoggable(Level.WARNING)) {
|
case WHITE_BISHOP: return "♗";
|
||||||
logger.warning("Ungültiger Zug: " + libMove);
|
case WHITE_KNIGHT: return "♘";
|
||||||
}
|
case WHITE_PAWN: return "♙";
|
||||||
return false;
|
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 String getOpeningName() {
|
public boolean isMated() {
|
||||||
if (detectedOpening != null) {
|
return board.isMated();
|
||||||
return detectedOpening.getEco() + " - " + detectedOpening.getName();
|
}
|
||||||
} else {
|
|
||||||
return "unbekannt";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String movesToUciString(List<Move> moves) {
|
public boolean isStalemate() {
|
||||||
StringBuilder sb = new StringBuilder();
|
return board.isStaleMate();
|
||||||
for (Move m : moves) {
|
}
|
||||||
sb.append(m.toString()).append(" ");
|
|
||||||
}
|
|
||||||
return sb.toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MoveDTO> getLegalDestinations(String from) {
|
public boolean isDraw() {
|
||||||
if (logger.isLoggable(Level.INFO)) {
|
return board.isDraw();
|
||||||
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() {
|
public String getCurrentPlayer() {
|
||||||
if (logger.isLoggable(Level.INFO)) {
|
return board.getSideToMove().toString(); // "WHITE" oder "BLACK"
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
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";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
|
||||||
|
|
||||||
public class MoveDTO {
|
|
||||||
private int fromRow, fromCol;
|
|
||||||
private int toRow, toCol;
|
|
||||||
|
|
||||||
public MoveDTO(int fromRow, int fromCol, int toRow, int toCol) {
|
|
||||||
this.fromRow = fromRow;
|
|
||||||
this.fromCol = fromCol;
|
|
||||||
this.toRow = toRow;
|
|
||||||
this.toCol = toCol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFromRow() {
|
|
||||||
return fromRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFromCol() {
|
|
||||||
return fromCol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getToRow() {
|
|
||||||
return toRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getToCol() {
|
|
||||||
return toCol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
|
||||||
|
|
||||||
public class PieceDTO {
|
|
||||||
private String type; // z.B. "KING", "PAWN"
|
|
||||||
private String color; // "WHITE" oder "BLACK"
|
|
||||||
private String unicodeSymbol;
|
|
||||||
|
|
||||||
public PieceDTO(String type, String color, String unicodeSymbol) {
|
|
||||||
this.type = type;
|
|
||||||
this.color = color;
|
|
||||||
this.unicodeSymbol = unicodeSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUnicodeSymbol() {
|
|
||||||
return unicodeSymbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.model;
|
|
||||||
|
|
||||||
public class PlayerDTO {
|
|
||||||
private String name;
|
|
||||||
private String color; // "WHITE" oder "BLACK"
|
|
||||||
|
|
||||||
public PlayerDTO(String name, String color) {
|
|
||||||
this.name = name;
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColor(String color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,254 +0,0 @@
|
||||||
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 du’s 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.view;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.GridBagConstraints;
|
|
||||||
import java.awt.GridBagLayout;
|
|
||||||
import java.awt.GridLayout;
|
|
||||||
import java.awt.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;
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
public GameGui(){
|
|
||||||
initFields();
|
|
||||||
mainFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public JFrame mainFrame() {
|
|
||||||
frame = new JFrame();
|
|
||||||
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
|
|
||||||
frame.setUndecorated(false);
|
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
frame.add(mainPanel());
|
|
||||||
|
|
||||||
|
|
||||||
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));
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
moveListScroll = new JScrollPane(moveListPanel);
|
|
||||||
moveListScroll.setPreferredSize(new Dimension(250, 800));
|
|
||||||
statsPanel.add(moveListScroll, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
// Button-Leiste (SÜD)
|
|
||||||
JPanel buttonPanel = new JPanel();
|
|
||||||
buttonPanel.setBackground(new Color(0x0d1b2a));
|
|
||||||
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);
|
|
||||||
btnSave.setBackground(new Color(0x218838)); btnSave.setForeground(Color.WHITE);
|
|
||||||
|
|
||||||
buttonPanel.add(btnFirst);
|
|
||||||
buttonPanel.add(btnPrev);
|
|
||||||
buttonPanel.add(btnNext);
|
|
||||||
buttonPanel.add(btnLast);
|
|
||||||
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();
|
|
||||||
for (String move : moves) {
|
|
||||||
JLabel moveLabel = new JLabel(move);
|
|
||||||
moveLabel.setForeground(Color.WHITE);
|
|
||||||
moveLabel.setFont(new Font("SansSerif", Font.PLAIN, 18));
|
|
||||||
moveListPanel.add(moveLabel);
|
|
||||||
}
|
|
||||||
moveListPanel.revalidate();
|
|
||||||
moveListPanel.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JLabel getField(int row, int col) {
|
|
||||||
return fields[row][col];
|
|
||||||
}
|
|
||||||
|
|
||||||
public JButton getFlipBoardButton() {
|
|
||||||
return flipBoardButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFlipped() {
|
|
||||||
return isFlipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFlipped(boolean flipped) {
|
|
||||||
this.isFlipped = flipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JButton getBtnFirst() { return btnFirst; }
|
|
||||||
public JButton getBtnPrev() { return btnPrev; }
|
|
||||||
public JButton getBtnNext() { return btnNext; }
|
|
||||||
public JButton getBtnLast() { return btnLast; }
|
|
||||||
public 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) {
|
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
package de.hs_mannheim.informatik.chess.view;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class MainGui {
|
|
||||||
|
|
||||||
private JFrame frame;
|
|
||||||
|
|
||||||
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.setExtendedState(JFrame.MAXIMIZED_BOTH);
|
|
||||||
frame.setUndecorated(false);
|
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
|
|
||||||
//Haupt-Panel mit GridBagLayout
|
|
||||||
JPanel mainPanel = new JPanel(new GridBagLayout());
|
|
||||||
mainPanel.setBackground(new Color(0xe0e1dd));
|
|
||||||
|
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
|
||||||
gbc.gridx = 0;
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
gbc.insets = new Insets(15, 0, 15, 0);
|
|
||||||
|
|
||||||
//Title
|
|
||||||
JLabel title = new JLabel("ChessDE", SwingConstants.CENTER);
|
|
||||||
title.setFont(new Font("Serif", Font.BOLD, 150));
|
|
||||||
title.setForeground(new Color(0x1b263b));
|
|
||||||
gbc.gridy = 0;
|
|
||||||
gbc.ipady = 50;
|
|
||||||
mainPanel.add(title, gbc);
|
|
||||||
|
|
||||||
//Abstand nach unten
|
|
||||||
gbc.gridy = 1;
|
|
||||||
gbc.ipady = 15;
|
|
||||||
mainPanel.add(Box.createRigidArea(new Dimension(0, 20)), gbc);
|
|
||||||
|
|
||||||
styleButton(btnMode1);
|
|
||||||
styleButton(btnCreative);
|
|
||||||
styleButton(btnLoadGame);
|
|
||||||
|
|
||||||
gbc.gridy = 1; gbc.ipady = 25;
|
|
||||||
mainPanel.add(btnMode1, gbc);
|
|
||||||
|
|
||||||
gbc.gridy = 2;
|
|
||||||
mainPanel.add(btnCreative, gbc);
|
|
||||||
|
|
||||||
gbc.gridy = 3;
|
|
||||||
mainPanel.add(btnLoadGame, gbc);
|
|
||||||
|
|
||||||
frame.add(mainPanel);
|
|
||||||
frame.setVisible(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
// --- 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));
|
|
||||||
btn.setBackground(new Color(0x778da9));
|
|
||||||
btn.setForeground(Color.WHITE);
|
|
||||||
btn.setFocusPainted(false);
|
|
||||||
btn.setBorder(BorderFactory.createEmptyBorder(10, 40, 10, 40));
|
|
||||||
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue