Browse Source

GameImages with marked last move

master
GAM 4 years ago
parent
commit
384ec0c331
3 changed files with 96 additions and 46 deletions
  1. +15
    -0
      pom.xml
  2. +77
    -42
      src/main/java/app/gameimage/GameImageGenerator.java
  3. +4
    -4
      src/main/java/app/gameimage/GameImageService.java

+ 15
- 0
pom.xml View File

@ -39,6 +39,11 @@
<enabled>false</enabled>
</snapshots>
</repository>
<!-- PGN parsing-->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<pluginRepositories>
@ -171,6 +176,16 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.github.bhlangonijr</groupId>
<artifactId>chesslib</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.beryx</groupId>
<artifactId>awt-color-factory</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>


+ 77
- 42
src/main/java/app/gameimage/GameImageGenerator.java View File

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

+ 4
- 4
src/main/java/app/gameimage/GameImageService.java View File

@ -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<String> getFen(Game game) {
if (game.getGameInfo().getFen() != null) return Optional.of(game.getGameInfo().getFen());
return chessComService.getChessComGame(game).map(ChessComGame::getFen);
private Optional<String> getPgn(Game game) {
if (game.getGameInfo().getPgn() != null) return Optional.of(game.getGameInfo().getPgn());
return chessComService.getChessComGame(game).map(ChessComGame::getPgn);
}
}

Loading…
Cancel
Save