| @ -1,77 +1,112 @@ | |||
| package app.gameimage; | |||
| import com.github.bhlangonijr.chesslib.*; | |||
| import com.github.bhlangonijr.chesslib.game.Game; | |||
| import com.github.bhlangonijr.chesslib.move.Move; | |||
| import com.github.bhlangonijr.chesslib.move.MoveList; | |||
| import com.github.bhlangonijr.chesslib.pgn.PgnHolder; | |||
| import org.beryx.awt.color.ColorFactory; | |||
| import java.awt.*; | |||
| import java.awt.image.BufferedImage; | |||
| import java.util.Objects; | |||
| import java.util.Set; | |||
| import java.util.List; | |||
| public class GameImageGenerator { | |||
| // TODO: mark squares of the last move using PGN | |||
| private static final int FIELD_SIZE = 150; | |||
| private static final Set<Character> WHITE_PIECES = Set.of('B', 'K', 'N', 'P', 'Q', 'R'); | |||
| private static final Set<Character> BLACK_PIECES = Set.of('b', 'k', 'n', 'p', 'q', 'r'); | |||
| private static final Set<Character> BLANKS = Set.of('1', '2', '3', '4', '5', '6', '7', '8'); | |||
| private static final Character ROW_DELIMITER = '/'; | |||
| private static final Color MARKED_WHITE_SQUARE_COLOR = ColorFactory.valueOf("#f7f769"); | |||
| private static final Color MARKED_BLACK_SQUARE_COLOR = ColorFactory.valueOf("#bbcb2b"); | |||
| private GameImageGenerator() { | |||
| } | |||
| public static BufferedImage generateImage(String fen) { | |||
| public static BufferedImage generateImage(String pgn) { | |||
| BufferedImage image = GameImageUtils.readImage("board"); | |||
| Graphics2D g = image.createGraphics(); | |||
| g.drawImage(image, 0, 0, null); | |||
| int x = 0; | |||
| int y = 0; | |||
| for (Character c : fen.split(" ")[0].toCharArray()) { | |||
| if (isPiece(c)) { | |||
| g.drawImage(GameImageUtils.readImage(getFilename(c)), x * FIELD_SIZE, y * FIELD_SIZE, null); | |||
| x += 1; | |||
| continue; | |||
| } | |||
| if (isRowDelimiter(c)) { | |||
| x = 0; | |||
| y += 1; | |||
| continue; | |||
| MoveList moves = getMoveList(pgn); | |||
| if (moves.size() >= 2) { | |||
| Board board = new Board(); | |||
| for (Move move: moves) { | |||
| board.doMove(move); | |||
| } | |||
| if (isBlank(c)) { | |||
| x += Character.getNumericValue(c); | |||
| continue; | |||
| Piece[] pieces = board.boardToArray(); | |||
| board.undoMove(); | |||
| Piece[] piecesSecondToLast = board.boardToArray(); | |||
| Square[] squares = Square.values(); | |||
| assert pieces.length >= 64; | |||
| assert squares.length >= 64; | |||
| for (int i = 0; i < 64; i++) { | |||
| int col = getColumnIndex(squares[i]); | |||
| int row = getRowIndex(squares[i]); | |||
| int x = col * FIELD_SIZE; | |||
| int y = row * FIELD_SIZE; | |||
| if (!pieces[i].equals(piecesSecondToLast[i])) { | |||
| g.setColor(col % 2 == row % 2 ? MARKED_WHITE_SQUARE_COLOR : MARKED_BLACK_SQUARE_COLOR); | |||
| g.fillRect(x, y, FIELD_SIZE, FIELD_SIZE); | |||
| } | |||
| if (pieces[i].getPieceType() != null && !pieces[i].getPieceType().equals(PieceType.NONE)) { | |||
| g.drawImage(GameImageUtils.readImage(getFilename(pieces[i])), x, y, null); | |||
| } | |||
| } | |||
| throw new IllegalArgumentException("FEN is invalid!"); | |||
| } | |||
| g.dispose(); | |||
| return image; | |||
| } | |||
| private static boolean isBlank(Character c) { | |||
| return BLANKS.contains(c); | |||
| private static MoveList getMoveList(String pgn) { | |||
| PgnHolder pgnHolder = new PgnHolder(""); | |||
| pgnHolder.loadPgn(pgn); | |||
| List<Game> games = pgnHolder.getGames(); | |||
| if (games.size() != 1) { | |||
| throw new IllegalArgumentException("PGN must contain exactly one game!"); | |||
| } | |||
| Game game = games.get(0); | |||
| try { | |||
| game.loadMoveText(); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return game.getHalfMoves(); | |||
| } | |||
| private static boolean isRowDelimiter(Character c) { | |||
| return Objects.equals(c, ROW_DELIMITER); | |||
| private static int getRowIndex(Square square) { | |||
| return 7 - square.getRank().ordinal(); | |||
| } | |||
| private static boolean isPiece(Character c) { | |||
| return isWhite(c) || isBlack(c); | |||
| private static int getColumnIndex(Square square) { | |||
| return square.getFile().ordinal(); | |||
| } | |||
| private static boolean isWhite(Character c) { | |||
| return WHITE_PIECES.contains(c); | |||
| private static String getFilename(Piece piece) { | |||
| return getSideLetter(piece) + getPieceLetter(piece); | |||
| } | |||
| private static boolean isBlack(Character c) { | |||
| return BLACK_PIECES.contains(c); | |||
| private static String getSideLetter(Piece piece) { | |||
| return piece.getPieceSide().equals(Side.WHITE) ? "w" : "b"; | |||
| } | |||
| private static String getFilename(Character piece) { | |||
| if (isWhite(piece)) { | |||
| return "w" + piece.toString().toLowerCase(); | |||
| } | |||
| if (isBlack(piece)) { | |||
| return "b" + piece.toString(); | |||
| private static String getPieceLetter(Piece piece) { | |||
| switch (piece.getPieceType()) { | |||
| case PAWN: | |||
| return "p"; | |||
| case KNIGHT: | |||
| return "n"; | |||
| case BISHOP: | |||
| return "b"; | |||
| case ROOK: | |||
| return "r"; | |||
| case QUEEN: | |||
| return "q"; | |||
| case KING: | |||
| return "k"; | |||
| default: | |||
| throw new IllegalArgumentException("This piece type is not valid!"); | |||
| } | |||
| throw new IllegalArgumentException("Character must represent a piece!"); | |||
| } | |||
| } | |||