Files
3DChess/app/src/main/java/chess/pgn/PgnExport.java
fl.du.pr Grens c741044469
All checks were successful
Linux arm64 / Build (push) Successful in 34s
doc, fin
2025-05-18 23:30:30 +02:00

238 lines
6.1 KiB
Java

package chess.pgn;
import java.util.List;
import chess.controller.CommandExecutor;
import chess.controller.PlayerCommand;
import chess.controller.Command.CommandResult;
import chess.controller.commands.CastlingCommand;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.GetPlayerMovesCommand;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.NewGameCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.event.EmptyGameDispatcher;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Move;
import chess.model.Piece;
import chess.model.pieces.Pawn;
/**
* Export a game to PGN format.
*/
public class PgnExport {
private static final PiecePgnName piecePgnName = new PiecePgnName();
private static Piece pieceAt(CommandExecutor commandExecutor, Coordinate coordinate) {
GetPieceAtCommand cmd = new GetPieceAtCommand(coordinate);
commandExecutor.executeCommand(cmd);
return cmd.getPiece();
}
private static List<Move> playerMoves(CommandExecutor commandExecutor) {
GetPlayerMovesCommand cmd = new GetPlayerMovesCommand();
commandExecutor.executeCommand(cmd);
return cmd.getMoves();
}
private static String gameEnd(Game game) {
switch (game.checkGameStatus(game.getPlayerTurn())) {
case Draw:
case Pat:
return "1/2-1/2";
case CheckMate:
if (game.getPlayerTurn() == Color.White)
return "0-1";
return "1-0";
default:
return "";
}
}
/**
* Resolve PGN ambiguity in the case of two pieces of the same type able to move on the same square.
* @param cmdExec the command executor attached to the game
* @param pieceMove move of the piece
* @return the character that solves the eventual ambiguity, empty if no ambiguity to start with
*/
private static String resolveAmbiguity(CommandExecutor cmdExec, Move pieceMove) {
Piece movingPiece = pieceAt(cmdExec, pieceMove.getStart());
assert movingPiece != null;
if (movingPiece instanceof Pawn)
return "";
List<Move> moves = playerMoves(cmdExec);
for (Move move : moves) {
if (move.equals(pieceMove) || !move.getFinish().equals(pieceMove.getFinish()))
continue;
Piece otherPiece = pieceAt(cmdExec, move.getStart());
// checking type of piece
if (!otherPiece.getClass().equals(movingPiece.getClass()))
continue;
String startPos = toString(pieceMove.getStart());
if (move.getStart().getX() != pieceMove.getStart().getX())
// not on the same column
return Character.toString(startPos.charAt(0));
else
return Character.toString(startPos.charAt(1));
}
return "";
}
/**
* From a move, get the capture-part of the associated PGN string.
* @param move the move
* @param movingPiece the piece that is moving
* @return the capture string of the PGN move
*/
private static String capture(MoveCommand move, Piece movingPiece) {
String result = "";
if (move.getDeadPiece() != null) {
if (movingPiece instanceof Pawn) {
result += toString(move.getMove().getStart()).charAt(0);
}
result += "x";
}
return result;
}
private static String promote(PlayerCommand nextCommand) {
if (nextCommand != null && nextCommand instanceof PromoteCommand promoteCommand) {
String result = "=";
result += switch (promoteCommand.getPromoteType()) {
case Bishop -> "B";
case Knight -> "N";
case Queen -> "Q";
case Rook -> "R";
};
return result;
}
return "";
}
private static String castling(CastlingCommand castlingCommand) {
String result = "O-O";
if (castlingCommand.isBigCastling())
result += "-O";
return result;
}
private static String checkCheckMate(Game game) {
switch (game.checkGameStatus(game.getPlayerTurn())) {
case CheckMate:
return "#";
case Check:
return "+";
default:
return "";
}
}
private record MoveResult(String move, CommandResult commandResult) {
}
private static MoveResult printMove(PlayerCommand cmd, PlayerCommand nextCommand, Game virtualGame,
CommandExecutor executor) {
String result = "";
if (cmd instanceof MoveCommand move) {
Piece movingPiece = virtualGame.getBoard().pieceAt(move.getMove().getStart());
assert movingPiece != null;
// piece name
result += piecePgnName.visit(movingPiece);
// ambiguious start
result += resolveAmbiguity(executor, move.getMove());
// capture
result += capture(move, movingPiece);
// end cell
result += toString(move.getMove().getFinish());
// promote
result += promote(nextCommand);
} else if (cmd instanceof CastlingCommand castlingCommand) {
result += castling(castlingCommand);
}
CommandResult commandResult = executor.executeCommand(cmd);
// check or checkmate
result += checkCheckMate(virtualGame);
result += " ";
return new MoveResult(result, commandResult);
}
public static String exportGame(Game game) {
Game virtualGame = new Game();
CommandExecutor executor = new CommandExecutor(virtualGame, new EmptyGameDispatcher());
executor.executeCommand(new NewGameCommand());
List<PlayerCommand> commands = game.getMoves();
String result = "";
int tour = 1;
String lastMove = null;
for (int i = 0; i < commands.size(); i++) {
PlayerCommand cmd = commands.get(i);
PlayerCommand nextCommand = null;
if (i != commands.size() - 1) {
nextCommand = commands.get(i + 1);
}
MoveResult moveResult = printMove(cmd, nextCommand, virtualGame, executor);
if (moveResult.commandResult() == CommandResult.Moved && virtualGame.getPlayerTurn() == Color.Black) {
result += tour + ".";
tour++;
}
if (moveResult.commandResult() == CommandResult.ActionNeeded) {
lastMove = moveResult.move();
continue;
}
if (lastMove != null && moveResult.commandResult() == CommandResult.Moved){
result += lastMove;
lastMove = null;
continue;
}
result += moveResult.move();
}
return result + " " + gameEnd(virtualGame);
}
public static String toString(Coordinate coordinate) {
String letters = "abcdefgh";
String numbers = "87654321";
return Character.toString(letters.charAt(coordinate.getX())) + numbers.charAt(coordinate.getY());
}
}