From 523eb094e127ca49894f4205aedeede73ac7715e Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Sun, 18 May 2025 12:18:09 +0200 Subject: [PATCH] 3d promote (Fixes #14) --- app/src/main/java/chess/OpenGLMain.java | 2 +- .../java/chess/ai/minimax/GameSimulation.java | 3 +- .../controller/commands/PromoteCommand.java | 2 +- .../controller/event/AsyncGameDispatcher.java | 4 +- .../controller/event/EmptyGameDispatcher.java | 2 +- .../chess/controller/event/GameAdapter.java | 2 +- .../chess/controller/event/GameListener.java | 4 +- .../java/chess/view/DDDrender/DDDView.java | 89 ++++++++++++++----- .../chess/view/DDDrender/world/World.java | 6 +- .../main/java/chess/view/audio/GameAudio.java | 3 +- .../java/chess/view/simplerender/Window.java | 2 +- 11 files changed, 82 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/chess/OpenGLMain.java b/app/src/main/java/chess/OpenGLMain.java index 1fadc76..2555f1b 100644 --- a/app/src/main/java/chess/OpenGLMain.java +++ b/app/src/main/java/chess/OpenGLMain.java @@ -11,7 +11,7 @@ public class OpenGLMain { Game game = new Game(); CommandExecutor commandExecutor = new CommandExecutor(game); - PgnFileSimulator fileSimulator = new PgnFileSimulator(commandExecutor, "games/FoolCheckmate.pgn"); + PgnFileSimulator fileSimulator = new PgnFileSimulator(commandExecutor, "games/PromoteTest.pgn"); DDDView ddd = new DDDView(commandExecutor); diff --git a/app/src/main/java/chess/ai/minimax/GameSimulation.java b/app/src/main/java/chess/ai/minimax/GameSimulation.java index 26c995f..9ae5ed5 100644 --- a/app/src/main/java/chess/ai/minimax/GameSimulation.java +++ b/app/src/main/java/chess/ai/minimax/GameSimulation.java @@ -11,6 +11,7 @@ import chess.controller.event.EmptyGameDispatcher; import chess.controller.event.GameAdapter; import chess.model.ChessBoard; import chess.model.Color; +import chess.model.Coordinate; import chess.model.Game; import chess.model.Move; import chess.model.PermissiveGame; @@ -26,7 +27,7 @@ public class GameSimulation extends GameAdapter implements CommandSender { } @Override - public void onPawnPromoted(PromoteType promotion) { + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { sendPawnPromotion(promotion); } diff --git a/app/src/main/java/chess/controller/commands/PromoteCommand.java b/app/src/main/java/chess/controller/commands/PromoteCommand.java index dcf755d..3d46060 100644 --- a/app/src/main/java/chess/controller/commands/PromoteCommand.java +++ b/app/src/main/java/chess/controller/commands/PromoteCommand.java @@ -56,7 +56,7 @@ public class PromoteCommand extends PlayerCommand { this.oldPawn = pawn; board.pieceComes(createPiece(this.promoteType, pawn.getColor()), this.pieceCoords); - outputSystem.onPawnPromoted(this.promoteType); + outputSystem.onPawnPromoted(this.promoteType, this.pieceCoords); // invalidate the last move cache board.setLastMove(null); diff --git a/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java b/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java index 0dfc6bf..e262a5e 100644 --- a/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java +++ b/app/src/main/java/chess/controller/event/AsyncGameDispatcher.java @@ -101,8 +101,8 @@ public class AsyncGameDispatcher extends GameDispatcher { } @Override - public void onPawnPromoted(PromoteType promotion) { - asyncForEachCall((l) -> l.onPawnPromoted(promotion)); + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { + asyncForEachCall((l) -> l.onPawnPromoted(promotion, coordinate)); } @Override diff --git a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java index c2ff7b8..ae212d7 100644 --- a/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java +++ b/app/src/main/java/chess/controller/event/EmptyGameDispatcher.java @@ -64,7 +64,7 @@ public class EmptyGameDispatcher extends GameDispatcher { } @Override - public void onPawnPromoted(PromoteType promotion) { + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { } @Override diff --git a/app/src/main/java/chess/controller/event/GameAdapter.java b/app/src/main/java/chess/controller/event/GameAdapter.java index 54e0189..e1141f7 100644 --- a/app/src/main/java/chess/controller/event/GameAdapter.java +++ b/app/src/main/java/chess/controller/event/GameAdapter.java @@ -50,6 +50,6 @@ public abstract class GameAdapter implements GameListener { public void onCastling(boolean bigCastling, Move kingMove, Move rookMove) {} @Override - public void onPawnPromoted(PromoteType promotion) {} + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) {} } diff --git a/app/src/main/java/chess/controller/event/GameListener.java b/app/src/main/java/chess/controller/event/GameListener.java index 83e68b1..129b046 100644 --- a/app/src/main/java/chess/controller/event/GameListener.java +++ b/app/src/main/java/chess/controller/event/GameListener.java @@ -99,8 +99,8 @@ public interface GameListener { * Invoked when a pawn is promoted * * @param promotion the type of promotion - * @param player the player who promoted the pawns + * @param coordinate the coordinate of the old pawn */ - void onPawnPromoted(PromoteType promotion); + void onPawnPromoted(PromoteType promotion, Coordinate coordinate); } diff --git a/app/src/main/java/chess/view/DDDrender/DDDView.java b/app/src/main/java/chess/view/DDDrender/DDDView.java index 6827a3a..684f3db 100644 --- a/app/src/main/java/chess/view/DDDrender/DDDView.java +++ b/app/src/main/java/chess/view/DDDrender/DDDView.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.function.Consumer; import chess.controller.commands.*; +import chess.controller.commands.PromoteCommand.PromoteType; import imgui.ImGui; import imgui.ImVec2; import imgui.flag.ImGuiCond; @@ -242,6 +243,15 @@ public class DDDView extends GameAdapter implements CommandSender { } } + private PieceEntity createDefault(Piece piece, Coordinate coordinate) throws IOException { + Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(coordinate); + Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); + + return new PieceEntity(piece, pieceWorldPos, + piece.getColor() == Color.White ? WHITE : BLACK, + piece.getColor() == Color.White ? 0.0f : (float) Math.PI); + } + private void initBoard() throws IOException { for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { @@ -250,14 +260,7 @@ public class DDDView extends GameAdapter implements CommandSender { if (piece == null) continue; - Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(pos); - Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); - - PieceEntity entity = new PieceEntity(piece, pieceWorldPos, - piece.getColor() == Color.White ? WHITE : BLACK, - piece.getColor() == Color.White ? 0.0f : (float) Math.PI); - - this.world.addPiece(entity, pos); + this.world.addPiece(createDefault(piece, pos), pos); } } this.boardEntity = new BoardEntity(); @@ -303,7 +306,7 @@ public class DDDView extends GameAdapter implements CommandSender { consumers.add(moveConsumer); this.window.addRegularTask(moveConsumer); } - + try { Thread.sleep((long) (animationTime * 1000.0f)); } catch (InterruptedException e) { @@ -315,16 +318,19 @@ public class DDDView extends GameAdapter implements CommandSender { final PieceEntity pEntity = pEntities.get(i); this.window.removeRegularTask(moveConsumer); - + Vector2f pieceDestBoardPos = DDDPlacement.coordinatesToVector(move.getFinish()); Vector3f pieceDestWorldPos = new Vector3f(pieceDestBoardPos.x(), 0, pieceDestBoardPos.y()); - - if (move.getDeadPieceCoords() != null) { - this.world.ejectPiece(move.getDeadPieceCoords()); - } + + final PieceEntity pDead = this.world.getPiece(move.getDeadPieceCoords()); + this.world.setPieceCoords(null, move.getDeadPieceCoords()); + + // we must do that on the rendering thread to avoid + // ConcurrentModificationException + this.window.scheduleTask(() -> this.world.ejectPiece(pDead)); pEntity.setPosition(pieceDestWorldPos); - + this.world.movePiece(pEntity, move); } @@ -371,25 +377,47 @@ public class DDDView extends GameAdapter implements CommandSender { } } - private void renderPopup(String title, String text) { + private void renderPopup(String title, Runnable content) { ImVec2 center = ImGui.getMainViewport().getCenter(); ImGui.setNextWindowPos(center, ImGuiCond.Appearing, new ImVec2(0.5f, 0.5f)); if (ImGui.beginPopupModal(title, null, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoMove)) { - ImGui.text(text); + content.run(); if (ImGui.button("Close")) { - ImGui.closeCurrentPopup(); - synchronized (this) { - notifyAll(); - } + closeCurrentPopup(); } ImGui.endPopup(); } } + private void closeCurrentPopup() { + ImGui.closeCurrentPopup(); + synchronized (this) { + notifyAll(); + } + } + + private void renderPopup(String title, String text) { + renderPopup(title, () -> ImGui.text(text)); + } + + private void renderPromoteDialog() { + renderPopup("Promotion", () -> { + ImGui.text("Select the promotion type :"); + for (PromoteType promoteType : PromoteType.values()) { + if (ImGui.button(promoteType.toString())) { + sendPawnPromotion(promoteType); + closeCurrentPopup(); + } + ImGui.sameLine(); + } + ImGui.newLine(); + }); + } + private void renderPopups() { renderPopup("Check", "Your king is in check"); renderPopup("Checkmate", "Checkmate, it's a win!"); - renderPopup("Promotion", "Please promote your pawn."); + renderPromoteDialog(); renderPopup("Pat", "It's a pat!"); renderPopup("Tie", "It's a tie!"); renderPopup("White surrender", "The white player has surrendered!"); @@ -451,4 +479,21 @@ public class DDDView extends GameAdapter implements CommandSender { public void onCastling(boolean bigCastling, Move kingMove, Move rookMove) { move3DPieces(List.of(kingMove, rookMove)); } + + @Override + public void onPromotePawn(Coordinate pieceCoords) { + openPopup("Promotion"); + } + + @Override + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { + this.window.scheduleTask(() -> { + this.world.ejectPiece(this.world.getPiece(coordinate)); + try { + this.world.addPiece(createDefault(getPieceAt(coordinate), coordinate), coordinate); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } } diff --git a/app/src/main/java/chess/view/DDDrender/world/World.java b/app/src/main/java/chess/view/DDDrender/world/World.java index bd67f94..22cfaef 100644 --- a/app/src/main/java/chess/view/DDDrender/world/World.java +++ b/app/src/main/java/chess/view/DDDrender/world/World.java @@ -36,7 +36,7 @@ public class World implements Closeable{ setPieceCoords(entity, coordinate); } - private void setPieceCoords(PieceEntity entity, Coordinate coordinate) { + public void setPieceCoords(PieceEntity entity, Coordinate coordinate) { pieces[coordinate.toIndex()] = entity; } @@ -45,11 +45,9 @@ public class World implements Closeable{ setPieceCoords(null, move.getStart()); } - public void ejectPiece(Coordinate coordinate) { - PieceEntity entity = getPiece(coordinate); + public void ejectPiece(PieceEntity entity) { if (entity != null) this.entites.remove(entity); - setPieceCoords(null, coordinate); } public PieceEntity getPiece(Coordinate coordinate) { diff --git a/app/src/main/java/chess/view/audio/GameAudio.java b/app/src/main/java/chess/view/audio/GameAudio.java index 02b379f..845ad65 100644 --- a/app/src/main/java/chess/view/audio/GameAudio.java +++ b/app/src/main/java/chess/view/audio/GameAudio.java @@ -2,6 +2,7 @@ package chess.view.audio; import chess.controller.commands.PromoteCommand.PromoteType; import chess.controller.event.GameAdapter; +import chess.model.Coordinate; import chess.model.Move; public class GameAudio extends GameAdapter { @@ -51,7 +52,7 @@ public class GameAudio extends GameAdapter { } @Override - public void onPawnPromoted(PromoteType promotion) { + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { playSound("promote"); } diff --git a/app/src/main/java/chess/view/simplerender/Window.java b/app/src/main/java/chess/view/simplerender/Window.java index f8a7b6e..6b23c5d 100644 --- a/app/src/main/java/chess/view/simplerender/Window.java +++ b/app/src/main/java/chess/view/simplerender/Window.java @@ -291,7 +291,7 @@ public class Window extends JFrame implements GameListener, CommandSender { public void onCastling(boolean bigCastling, Move kingMove, Move rookMove) {} @Override - public void onPawnPromoted(PromoteType promotion) {} + public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) {} @Override public CommandExecutor getCommandExecutor() {