diff --git a/pom.xml b/pom.xml
index 330bdcc..daa44f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,11 @@
false
+
+
+ jitpack.io
+ https://jitpack.io
+
@@ -171,6 +176,16 @@
org.projectlombok
lombok
+
+ com.github.bhlangonijr
+ chesslib
+ 1.3.0
+
+
+ org.beryx
+ awt-color-factory
+ 1.0.2
+
diff --git a/src/main/java/app/gameimage/GameImageGenerator.java b/src/main/java/app/gameimage/GameImageGenerator.java
index a5465b6..b018091 100644
--- a/src/main/java/app/gameimage/GameImageGenerator.java
+++ b/src/main/java/app/gameimage/GameImageGenerator.java
@@ -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 WHITE_PIECES = Set.of('B', 'K', 'N', 'P', 'Q', 'R');
- private static final Set BLACK_PIECES = Set.of('b', 'k', 'n', 'p', 'q', 'r');
- private static final Set 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 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!");
}
}
diff --git a/src/main/java/app/gameimage/GameImageService.java b/src/main/java/app/gameimage/GameImageService.java
index ca5539d..413a37f 100644
--- a/src/main/java/app/gameimage/GameImageService.java
+++ b/src/main/java/app/gameimage/GameImageService.java
@@ -33,7 +33,7 @@ public class GameImageService {
public void createImageIfNotPresent(Game game) {
if (hasImage(game)) return;
- getFen(game).ifPresent(fen -> GameImageUtils.writeImage(generateImage(fen), getGameId(game)));
+ getPgn(game).ifPresent(pgn -> GameImageUtils.writeImage(generateImage(pgn), getGameId(game)));
}
public Image getImage(Game game, String alt) {
@@ -41,9 +41,9 @@ public class GameImageService {
return new Image(resource, alt);
}
- private Optional getFen(Game game) {
- if (game.getGameInfo().getFen() != null) return Optional.of(game.getGameInfo().getFen());
- return chessComService.getChessComGame(game).map(ChessComGame::getFen);
+ private Optional getPgn(Game game) {
+ if (game.getGameInfo().getPgn() != null) return Optional.of(game.getGameInfo().getPgn());
+ return chessComService.getChessComGame(game).map(ChessComGame::getPgn);
}
}