From 384ec0c331414ef217c49aeb6ce8387a8d261a4e Mon Sep 17 00:00:00 2001 From: GAM Date: Sun, 18 Apr 2021 11:41:55 +0200 Subject: [PATCH] GameImages with marked last move --- pom.xml | 15 +++ .../app/gameimage/GameImageGenerator.java | 119 +++++++++++------- .../java/app/gameimage/GameImageService.java | 8 +- 3 files changed, 96 insertions(+), 46 deletions(-) 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); } }