From 5e60c2deb1f192dc2f082608dd4390ff3e873c5a Mon Sep 17 00:00:00 2001 From: Roscoe Date: Sun, 4 May 2025 03:31:37 +0100 Subject: [PATCH] Add initial code --- .gitignore | 41 ++++++++++ .idea/.gitignore | 10 +++ .idea/encodings.xml | 7 ++ .idea/misc.xml | 14 ++++ .idea/vcs.xml | 6 ++ pom.xml | 42 +++++++++++ src/main/java/moe/wah/Main.java | 130 ++++++++++++++++++++++++++++++++ 7 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 pom.xml create mode 100644 src/main/java/moe/wah/Main.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8baad2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + +/tmp/ +*.png \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7443e22 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml + +/discord.xml \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8ccaa52 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7594b4d --- /dev/null +++ b/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + moe.wah + PGN-ator + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + + + + + jitpack.io + https://jitpack.io + + + + + + com.github.bhlangonijr + chesslib + 1.3.4 + + + com.github.alexandreroman + chessimage + 1.0.0 + + + com.sksamuel.scrimage + scrimage-core + 4.3.1 + + + + \ No newline at end of file diff --git a/src/main/java/moe/wah/Main.java b/src/main/java/moe/wah/Main.java new file mode 100644 index 0000000..b5de26e --- /dev/null +++ b/src/main/java/moe/wah/Main.java @@ -0,0 +1,130 @@ +package moe.wah; + +import com.github.alexandreroman.chessimage.ChessRenderer; +import com.github.alexandreroman.chessimage.ChessThemeLibrary; +import com.github.bhlangonijr.chesslib.Board; +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 com.sksamuel.scrimage.ImmutableImage; +import com.sksamuel.scrimage.nio.StreamingGifWriter; +import com.sksamuel.scrimage.nio.StreamingGifWriter.GifStream; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Main { + public static void main(String[] args) throws Exception { + Files.createDirectories(Paths.get("./temp/")); + // Load PGN from file + PgnHolder pgn = new PgnHolder("game.pgn"); + pgnToGif(pgn); + } + private static void pgnToGif(PgnHolder pgn) throws Exception { + ChessRenderer renderer = new ChessRenderer(ChessThemeLibrary.GREEN_THEME, 60); + Game game = pgnToGame(pgn); + String boardID = String.valueOf(Instant.now().toEpochMilli()); + game.loadMoveText(); + System.out.println(game.getTermination()); + + MoveList moves = game.getHalfMoves(); + Board board = new Board(); + int index = 1; + for (Move move : moves) { + board.doMove(move); + renderBoard(board, renderer, boardID, index); + + // Add 5 extra frames of final board + if (index == moves.size()) { + for (int x = 0; x < 5; x++) { + index++; + renderBoard(board, renderer, boardID, index); + } + } + index++; + } + + System.out.println("Rendered " + moves.size() + " moves"); + + boardToGif(boardID); + } + + private static void renderBoard(Board board, ChessRenderer renderer, String boardID, int index) { + String index_padded = String.format("%03d", index); + File targetFile = new File("./temp/", "board_" + boardID + "_" + index_padded + ".png"); + try (FileOutputStream out = new FileOutputStream(targetFile)) { + renderer.render(board.getFen(), out); + } catch (IOException e) { + System.out.println("Error rendering move " + index_padded + ", " + e.getMessage()); + } + } + + private static Game pgnToGame(PgnHolder pgn) throws Exception { + pgn.loadPgn(); + return pgn.getGames().getFirst(); + } + + /** + * Converts a chess board to a GIF + * Output file is board_x.gif where x is the boardID + * @param boardID ID of the board (UNIX epoch) + */ + public static void boardToGif(String boardID) throws Exception { + List boardFiles = getBoardFiles(new File("./temp/"), boardID); + if (boardFiles.isEmpty()) { + throw new RuntimeException("No PNG files found for board with ID: " + boardID); + } + List images = new ArrayList<>(); + for (File file : boardFiles) { + BufferedImage image = ImageIO.read(file); + images.add(image); + } + System.out.println("There are " + (long) images.size() + " frames for board " + boardID); + String gif_name = "board_" + boardID + ".gif"; + StreamingGifWriter writer = new StreamingGifWriter(Duration.ofMillis(500), true, false); + GifStream gif = writer.prepareStream(gif_name, BufferedImage.TYPE_INT_ARGB); + for (BufferedImage image : images) { + gif.writeFrame(ImmutableImage.fromAwt(image)); + } + gif.close(); + System.out.println("Rendered " + gif_name); + for (File file : boardFiles) { + boolean delete_success = file.delete(); + if (!delete_success) { + System.out.println("Failed to delete " + file.getName() + "!"); + } + } + System.out.println("Removed " + boardFiles.size() + " temp files"); + } + + /** + * Gets the PNG files for the specified board + * @param directory Directory to search + * @param boardID Board ID (UNIX epoch) + * @return A list of board Files + */ + private static List getBoardFiles(File directory, String boardID) { + List boardFiles = new ArrayList<>(); + File[] files = directory.listFiles(); + assert files != null; + Arrays.sort(files); + for (File file : files) { + if (file.isFile() && file.getName().toLowerCase().endsWith(".png") + && file.getName().startsWith("board_" + boardID)) { + boardFiles.add(file); + } + } + return boardFiles; + } +} \ No newline at end of file