Compare commits

..

2 Commits

Author SHA1 Message Date
Justin Muravjev cf870fec57 Merge remote-tracking branch 'origin/Main' into PMD-Korrektur 2025-06-24 10:53:19 +02:00
valen 49e88ac03e Korrektur von ChessEngine 2025-06-24 10:26:17 +02:00
2 changed files with 516 additions and 440 deletions

View File

@ -33,13 +33,12 @@
<version>1.3.4</version> <version>1.3.4</version>
</dependency> </dependency>
<!-- JUnit 5 -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.10.2</version> <version>5.10.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Mockito --> <!-- Mockito -->
<dependency> <dependency>
@ -48,9 +47,50 @@
<version>5.11.0</version> <version>5.11.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.4.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.12.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.6.1</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins> <plugins>
<!-- Java Compiler Plugin --> <!-- Java Compiler Plugin -->
<plugin> <plugin>

View File

@ -1,11 +1,10 @@
package de.hs_mannheim.informatik.chess.model; package de.hs_mannheim.informatik.chess.model;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; 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.ConsoleHandler;
@ -21,10 +20,8 @@ import com.github.bhlangonijr.chesslib.move.Move;
import com.github.bhlangonijr.chesslib.pgn.PgnHolder; import com.github.bhlangonijr.chesslib.pgn.PgnHolder;
import com.github.bhlangonijr.chesslib.game.*; import com.github.bhlangonijr.chesslib.game.*;
import com.github.bhlangonijr.chesslib.move.MoveList; import com.github.bhlangonijr.chesslib.move.MoveList;
import java.time.LocalDate;
import com.github.bhlangonijr.chesslib.Side; 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 List<Move> moves = new ArrayList<>();
@ -32,457 +29,496 @@ public class ChessEngine {
private static final Logger logger = Logger.getLogger(ChessEngine.class.getName()); private static final Logger logger = Logger.getLogger(ChessEngine.class.getName());
private int quicksaveWhiteTimeLeft = -1; private int quicksaveWhiteTimeLeft = -1;
private int quicksaveBlackTimeLeft = -1; private int quicksaveBlackTimeLeft = -1;
private int currentMoveIndex = 0; private int currentMoveIndex = 0;
private Timer whiteTimer; private Timer whiteTimer;
private Timer blackTimer; private Timer blackTimer;
private final GameMode mode; private final GameMode mode;
private String quicksaveFen = null; private String quicksaveFen = null;
private List<Move> quicksaveMoves = null; private List<Move> quicksaveMoves = null;
private int quicksaveMoveIndex = 0; private int quicksaveMoveIndex = 0;
private Opening detectedOpening = null; private Opening detectedOpening = null;
public ChessEngine() { public ChessEngine() {
this.mode = null; this.mode = null;
logging();
board = new Board();
}
public ChessEngine(GameMode mode) {
this.mode = mode;
whiteTimer = new Timer(mode.minutes, mode.seconds);
blackTimer = new Timer(mode.minutes, mode.seconds);
logging(); logging();
board = new Board(); board = new Board();
} }
public boolean move(MoveDTO move) {
String from = "" + (char)('A' + move.getFromCol()) + (8 - move.getFromRow());
String to = "" + (char)('A' + move.getToCol()) + (8 - move.getToRow());
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
if (board.legalMoves().contains(libMove)) {
board.doMove(libMove);
// Replay? Dann abschneiden public ChessEngine(GameMode mode) {
if (currentMoveIndex < moves.size()) { this.mode = mode;
logger.info("Replay-Modus: Züge nach " + currentMoveIndex + " werden entfernt."); whiteTimer = new Timer(mode.minutes, mode.seconds);
moves = new ArrayList<>(moves.subList(0, currentMoveIndex)); blackTimer = new Timer(mode.minutes, mode.seconds);
} logging();
moves.add(libMove); board = new Board();
currentMoveIndex++; }
logger.info("Zug erfolgreich durchgeführt: " + libMove);
// Opening-Erkennung nach jedem erfolgreichen Zug public boolean move(MoveDTO move) {
String playedMovesUci = movesToUciString(moves); String from = "" + (char) ('A' + move.getFromCol()) + (8 - move.getFromRow());
detectedOpening = Opening.detect(playedMovesUci); String to = "" + (char) ('A' + move.getToCol()) + (8 - move.getToRow());
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to));
if (board.legalMoves().contains(libMove)) {
board.doMove(libMove);
if (detectedOpening != null) { if (currentMoveIndex < moves.size()) {
logger.info("Aktuelles Opening erkannt: " + detectedOpening.getEco() + " - " + detectedOpening.getName()); if (logger.isLoggable(Level.INFO)) {
// Optional: Speichere das Opening in ein Feld, falls benötigt logger.info("Replay-Modus: Züge nach " + currentMoveIndex + " werden entfernt.");
} }
return true; moves = new ArrayList<>(moves.subList(0, currentMoveIndex));
} }
logger.warning("Ungültiger Zug: " + libMove); moves.add(libMove);
return false; currentMoveIndex++;
} if (logger.isLoggable(Level.INFO)) {
logger.info("Zug erfolgreich durchgeführt: " + libMove);
}
public String getOpeningName() { String playedMovesUci = movesToUciString(moves);
if (detectedOpening != null) { detectedOpening = Opening.detect(playedMovesUci);
return detectedOpening.getEco() + " - " + detectedOpening.getName();
} else {
return "unbekannt";
}
}
private String movesToUciString(List<Move> moves) { if (detectedOpening != null && logger.isLoggable(Level.INFO)) {
StringBuilder sb = new StringBuilder(); logger.info("Aktuelles Opening erkannt: " + detectedOpening.getEco() + " - " + detectedOpening.getName());
for (Move m : moves) { }
sb.append(m.toString()).append(" "); return true;
} }
return sb.toString().trim(); if (logger.isLoggable(Level.WARNING)) {
} logger.warning("Ungültiger Zug: " + libMove);
}
return false;
}
public List<MoveDTO> getLegalDestinations(String from) { public String getOpeningName() {
logger.info("Hole legale Züge von: " + from); if (detectedOpening != null) {
List<MoveDTO> destinations = new ArrayList<>(); return detectedOpening.getEco() + " - " + detectedOpening.getName();
try { } else {
Square fromSq = Square.valueOf(from.toUpperCase()); return "unbekannt";
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));
}
}
logger.info("Es wurden " + destinations.size() + " Ziele gefunden.");
} catch (Exception e) {
logger.severe("Fehler beim Holen der legalen Ziele: " + e.getMessage());
}
return destinations;
}
public List<String> getMoveListStringsGrouped() { private String movesToUciString(List<Move> moves) {
logger.info("Gruppiere Züge für Anzeige."); StringBuilder sb = new StringBuilder();
List<String> result = new ArrayList<>(); for (Move m : moves) {
StringBuilder sb = new StringBuilder(); sb.append(m.toString()).append(" ");
for (int i = 0; i < moves.size(); i++) { }
if (i % 2 == 0) sb.append((i/2 + 1) + ". "); return sb.toString().trim();
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() { public List<MoveDTO> getLegalDestinations(String from) {
this.quicksaveFen = board.getFen(); if (logger.isLoggable(Level.INFO)) {
this.quicksaveMoves = new ArrayList<>(moves); logger.info("Hole legale Züge von: " + from);
this.quicksaveMoveIndex = currentMoveIndex; }
if (whiteTimer != null) quicksaveWhiteTimeLeft = whiteTimer.getSecondsLeft(); List<MoveDTO> destinations = new ArrayList<>();
if (blackTimer != null) quicksaveBlackTimeLeft = blackTimer.getSecondsLeft(); try {
} Square fromSq = Square.valueOf(from.toUpperCase(java.util.Locale.ROOT));
public boolean quickload() { for (Move move : board.legalMoves()) {
if (quicksaveFen == null) return false; if (move.getFrom().equals(fromSq)) {
board = new Board(); int fromRow = 8 - fromSq.getRank().ordinal() - 1;
board.loadFromFen(quicksaveFen); int fromCol = fromSq.getFile().ordinal();
moves = new ArrayList<>(quicksaveMoves); int toRow = 8 - move.getTo().getRank().ordinal() - 1;
currentMoveIndex = quicksaveMoveIndex; int toCol = move.getTo().getFile().ordinal();
if (whiteTimer != null && quicksaveWhiteTimeLeft != -1) whiteTimer.setSecondsLeft(quicksaveWhiteTimeLeft); destinations.add(new MoveDTO(fromRow, fromCol, toRow, toCol));
if (blackTimer != null && quicksaveBlackTimeLeft != -1) blackTimer.setSecondsLeft(quicksaveBlackTimeLeft); }
return true; }
} 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 void clearQuicksave() { public List<String> getMoveListStringsGrouped() {
quicksaveFen = null; if (logger.isLoggable(Level.INFO)) {
quicksaveMoves = null; logger.info("Gruppiere Züge für Anzeige.");
quicksaveMoveIndex = 0; }
} 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 PieceDTO getPieceAt(String square) { public boolean quickload() {
logger.info("Hole Figur an Feld: " + square); if (quicksaveFen == null) {
Piece piece = board.getPiece(Square.valueOf(square.toUpperCase())); return false;
return convertPieceToDTO(piece); }
} 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 boolean moveWithPromotion(MoveDTO move, String promotionPiece) { public void clearQuicksave() {
String from = "" + (char)('A' + move.getFromCol()) + (8 - move.getFromRow()); quicksaveFen = null;
String to = "" + (char)('A' + move.getToCol()) + (8 - move.getToRow()); quicksaveMoves = null;
quicksaveMoveIndex = 0;
}
// Die Farbe bestimmen! public PieceDTO getPieceAt(String square) {
boolean isWhite = (8 - move.getFromRow()) < (8 - move.getToRow()); if (logger.isLoggable(Level.INFO)) {
Piece promotion; logger.info("Hole Figur an Feld: " + square);
switch (promotionPiece) { }
case "ROOK": promotion = isWhite ? Piece.WHITE_ROOK : Piece.BLACK_ROOK; break; Piece piece = board.getPiece(Square.valueOf(square.toUpperCase(java.util.Locale.ROOT)));
case "KNIGHT": promotion = isWhite ? Piece.WHITE_KNIGHT : Piece.BLACK_KNIGHT; break; return convertPieceToDTO(piece);
case "BISHOP": promotion = isWhite ? Piece.WHITE_BISHOP : Piece.BLACK_BISHOP; break; }
default: promotion = isWhite ? Piece.WHITE_QUEEN : Piece.BLACK_QUEEN;
}
Move libMove = new Move(Square.valueOf(from), Square.valueOf(to), promotion); 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());
if (board.legalMoves().contains(libMove)) { boolean isWhite = (8 - move.getFromRow()) < (8 - move.getToRow());
board.doMove(libMove); 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;
}
if (currentMoveIndex < moves.size()) { Move libMove = new Move(Square.valueOf(from), Square.valueOf(to), promotion);
moves = new ArrayList<>(moves.subList(0, currentMoveIndex));
}
moves.add(libMove);
currentMoveIndex++;
logger.info("Promotionszug durchgeführt: " + libMove);
return true;
}
logger.warning("Ungültiger Promotionszug: " + libMove);
return false;
}
public BoardDTO getBoardAsDTO() { if (board.legalMoves().contains(libMove)) {
logger.info("Erstelle DTO-Abbild des Boards"); board.doMove(libMove);
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) { if (currentMoveIndex < moves.size()) {
switch (piece) { moves = new ArrayList<>(moves.subList(0, currentMoveIndex));
case WHITE_KING: return "♔"; }
case WHITE_QUEEN: return "♕"; moves.add(libMove);
case WHITE_ROOK: return "♖"; currentMoveIndex++;
case WHITE_BISHOP: return "♗"; if (logger.isLoggable(Level.INFO)) {
case WHITE_KNIGHT: return "♘"; logger.info("Promotionszug durchgeführt: " + libMove);
case WHITE_PAWN: return "♙"; }
case BLACK_KING: return "♚"; return true;
case BLACK_QUEEN: return "♛"; }
case BLACK_ROOK: return "♜"; if (logger.isLoggable(Level.WARNING)) {
case BLACK_BISHOP: return "♝"; logger.warning("Ungültiger Promotionszug: " + libMove);
case BLACK_KNIGHT: return "♞"; }
case BLACK_PAWN: return "♟"; return false;
default: return " "; }
}
}
public void setPositionToMoveIndex(int idx) { public BoardDTO getBoardAsDTO() {
logger.info("Setze Board auf Zug-Index: " + idx); if (logger.isLoggable(Level.INFO)) {
board = new Board(); logger.info("Erstelle DTO-Abbild des Boards");
board.loadFromFen(initialFen); // Statt new Board() -> initialFen! }
for (int i = 0; i < idx; i++) { PieceDTO[][] dtoBoard = new PieceDTO[8][8];
board.doMove(moves.get(i)); for (int rank = 8; rank >= 1; rank--) {
} for (int file = 0; file < 8; file++) {
currentMoveIndex = idx; Square square = Square.valueOf("" + (char) ('A' + file) + rank);
Piece piece = board.getPiece(square);
dtoBoard[8 - rank][file] = convertPieceToDTO(piece);
}
}
return new BoardDTO(dtoBoard);
}
String playedMovesUci = movesToUciString(moves.subList(0, idx)); private String pieceToUnicode(Piece piece) {
detectedOpening = Opening.detect(playedMovesUci); switch (piece) {
} case WHITE_KING:
public int getCurrentMoveIndex() { return "♔";
logger.info("Hole aktuellen Zug-Index: " + currentMoveIndex); case WHITE_QUEEN:
return currentMoveIndex; 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 int getMoveListSize() { public void setPositionToMoveIndex(int idx) {
logger.info("Hole Anzahl gespielter Züge: " + moves.size()); if (logger.isLoggable(Level.INFO)) {
return moves.size(); 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;
private PieceDTO convertPieceToDTO(Piece piece) { String playedMovesUci = movesToUciString(moves.subList(0, idx));
if (piece == null || piece.equals(Piece.NONE)) return null; detectedOpening = Opening.detect(playedMovesUci);
String color = piece.name().startsWith("WHITE") ? "WHITE" : "BLACK"; }
String type = piece.name().substring(piece.name().indexOf('_') + 1); // "PAWN", "KING"...
String symbol = pieceToUnicode(piece);
return new PieceDTO(type, color, symbol);
}
public void setPositionFromFEN(String fen) { public int getCurrentMoveIndex() {
board.loadFromFen(fen); if (logger.isLoggable(Level.INFO)) {
initialFen = fen; 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();
}
public boolean isMated() { private PieceDTO convertPieceToDTO(Piece piece) {
boolean mated = board.isMated(); if (piece == null || piece.equals(Piece.NONE)) {
logger.info("isMated() = " + mated); return null;
return mated; }
} 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 boolean isStalemate() { public void setPositionFromFEN(String fen) {
boolean stale = board.isStaleMate(); board.loadFromFen(fen);
logger.info("isStalemate() = " + stale); initialFen = fen;
return stale; }
}
public boolean isDraw() { public boolean isMated() {
boolean draw = board.isDraw(); boolean mated = board.isMated();
logger.info("isDraw() = " + draw); if (logger.isLoggable(Level.INFO)) {
return draw; logger.info("isMated() = " + mated);
} }
return mated;
}
public String getCurrentPlayer() { public boolean isStalemate() {
String player = board.getSideToMove().toString(); boolean stale = board.isStaleMate();
logger.info("Am Zug: " + player); if (logger.isLoggable(Level.INFO)) {
return player; logger.info("isStalemate() = " + stale);
} }
return stale;
}
public void logging() { public boolean isDraw() {
boolean draw = board.isDraw();
if (logger.isLoggable(Level.INFO)) {
logger.info("isDraw() = " + draw);
}
return draw;
}
// Eigener Handler nur für diese Klasse public String getCurrentPlayer() {
ConsoleHandler handler = new ConsoleHandler(); String player = board.getSideToMove().toString();
handler.setLevel(Level.ALL); if (logger.isLoggable(Level.INFO)) {
handler.setFormatter(new SimpleFormatter() { logger.info("Am Zug: " + player);
@Override }
public synchronized String format(LogRecord lr) { return player;
return String.format("[%s] %s%n%n", lr.getLevel().getLocalizedName(), lr.getMessage()); }
}
});
logger.setUseParentHandlers(false);
logger.addHandler(handler);
logger.info("ChessEngine wurde initialisiert."); 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);
public List<Game> loadGamesFromPgn(String path) throws IOException { if (logger.isLoggable(Level.INFO)) {
logger.info("ChessEngine wurde initialisiert.");
}
}
PgnHolder pgnHolder = new PgnHolder(path); public List<Game> loadGamesFromPgn(String path) throws IOException {
try { PgnHolder pgnHolder = new PgnHolder(path);
try {
pgnHolder.loadPgn(); pgnHolder.loadPgn();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
List<Game> games = pgnHolder.getGames(); return pgnHolder.getGames();
return games;
}
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) {
// Sicher alle Strings holen (nie null)
String event = safe(game.getRound().getEvent().getName());
String site = safe(game.getRound().getEvent().getSite());
String round = "" + game.getRound().getNumber();
// Datum für PGN-Format (YYYY.MM.DD)
String date = safe(game.getRound().getEvent().getStartDate()).replace("-", ".");
String wName = safe(game.getWhitePlayer().getName());
String bName = safe(game.getBlackPlayer().getName());
String result = safe(game.getResult().getDescription());
// PGN-Header zusammenbauen
StringBuilder header = new StringBuilder();
header.append("[Event \"" + event + "\"]\n");
header.append("[Site \"" + site + "\"]\n");
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");
// Züge als SAN holen
StringBuilder moves = new StringBuilder();
String[] sanArray = game.getHalfMoves().toSanArray();
for (int i = 0; i < sanArray.length; i++) {
if (i % 2 == 0) {
moves.append((i / 2 + 1)).append(". ");
}
moves.append(sanArray[i]).append(" ");
// Optional: Zeilenumbruch für Lesbarkeit
// if (i > 0 && i % 8 == 0) moves.append("\n");
}
moves.append(result); // Ergebnis am Ende!
String file = header + moves.toString();
// Datei schreiben
try {
Files.writeString(Path.of(path, dateiname), file, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
// Hilfsfunktion für Null-Sicherheit
private String safe(String s) {
return s == null ? "?" : s;
}
public Game getCurrentGame() {
return getCurrentGame(this.board, this.moves, this.currentMoveIndex);
}
public Game getCurrentGame(Board board, java.util.List<Move> moves, int currentMoveIndex) {
// Event und Turnierdaten setzen
Event event = new Event();
event.setName("Generated Game");
event.setSite("Local");
event.setStartDate(LocalDate.now().toString()); // Format: yyyy-MM-dd
// Runde anlegen
Round round = new Round(event);
round.setNumber(1);
// Spiel initialisieren
Game game = new Game("1", round); // "1" ist die Game-ID
// Spieler setzen (deine MyPlayer-Klasse)
game.setWhitePlayer(new MyPlayer("White"));
game.setBlackPlayer(new MyPlayer("Black"));
// Ergebnis setzen
if (board.isMated()) {
game.setResult(board.getSideToMove() == Side.WHITE ? GameResult.BLACK_WON : GameResult.WHITE_WON);
} else if (board.isStaleMate() || board.isDraw()) {
game.setResult(GameResult.DRAW);
} else {
game.setResult(GameResult.ONGOING);
}
// Züge übernehmen
MoveList moveList = new MoveList();
for (Move move : moves) {
moveList.add(move);
}
game.setHalfMoves(moveList);
// Position auf aktuellen Zug setzen (letzter gespielter Halbzug)
if (currentMoveIndex > 0 && currentMoveIndex <= moveList.size()) {
game.setPosition(currentMoveIndex - 1);
} else {
game.setPosition(moveList.size() - 1);
}
// FEN setzen: JETZT das aktuelle Board-FEN verwenden!
game.setBoard(new Board());
game.getBoard().loadFromFen(board.getFen());
return game;
}
public void undoLastMove() {
if (currentMoveIndex > 0 && moves.size() > 0) {
board.undoMove(); // 1. Brett zurücksetzen
moves.remove(currentMoveIndex - 1); // 2. Zug aus Move-Liste löschen
currentMoveIndex--; // 3. Index anpassen
// 4. Erkennung Opening neu machen!
String playedMovesUci = movesToUciString(moves.subList(0, currentMoveIndex));
detectedOpening = Opening.detect(playedMovesUci);
}
}
public void loadMoves(List<Move> moveList) {
board = new Board(); // Neues leeres Brett
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() {
// TODO Auto-generated method stub
return mode;
} }
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;
}
} }