|
|
@ -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!"); |
|
|
|
} |
|
|
|
} |