| @ -1,77 +1,112 @@ | |||||
| package app.gameimage; | 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.*; | ||||
| import java.awt.image.BufferedImage; | import java.awt.image.BufferedImage; | ||||
| import java.util.Objects; | |||||
| import java.util.Set; | |||||
| import java.util.List; | |||||
| public class GameImageGenerator { | public class GameImageGenerator { | ||||
| // TODO: mark squares of the last move using PGN | |||||
| private static final int FIELD_SIZE = 150; | 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() { | private GameImageGenerator() { | ||||
| } | } | ||||
| public static BufferedImage generateImage(String fen) { | |||||
| public static BufferedImage generateImage(String pgn) { | |||||
| BufferedImage image = GameImageUtils.readImage("board"); | BufferedImage image = GameImageUtils.readImage("board"); | ||||
| Graphics2D g = image.createGraphics(); | Graphics2D g = image.createGraphics(); | ||||
| g.drawImage(image, 0, 0, null); | 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; | 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!"); | |||||
| } | } | ||||
| } | } | ||||