diff --git a/app/src/main/java/chess/controller/commands/GetAllowedMovesPieceCommand.java b/app/src/main/java/chess/controller/commands/GetAllowedMovesPieceCommand.java index 331abff..b212116 100644 --- a/app/src/main/java/chess/controller/commands/GetAllowedMovesPieceCommand.java +++ b/app/src/main/java/chess/controller/commands/GetAllowedMovesPieceCommand.java @@ -34,7 +34,7 @@ public class GetAllowedMovesPieceCommand extends Command { if (piece.getColor() != game.getPlayerTurn()) return CommandResult.NotAllowed; - this.destinations = board.getAllowedMoves(start); + this.destinations = board.getPieceAllowedMoves(start); return CommandResult.NotMoved; } diff --git a/app/src/main/java/chess/model/ChessBoard.java b/app/src/main/java/chess/model/ChessBoard.java index 920cef1..9031242 100644 --- a/app/src/main/java/chess/model/ChessBoard.java +++ b/app/src/main/java/chess/model/ChessBoard.java @@ -54,6 +54,10 @@ public class ChessBoard { this.kingPos = new Coordinate[Color.values().length]; } + /** + * Apply a move on the board. + * @param move move to apply + */ public void applyMove(Move move) { assert move.isValid() : "Invalid move !"; Piece deadPiece = pieceAt(move.getDeadPieceCoords()); @@ -70,12 +74,20 @@ public class ChessBoard { this.lastVirtualMove = move; } + /** + * Undo the last move + */ public void undoLastMove() { assert this.lastVirtualMove != null : "Can't undo at the beginning!"; undoMove(this.lastVirtualMove, this.lastEjectedPiece); } + /** + * Undo the specified move + * @param move + * @param deadPiece + */ public void undoMove(Move move, Piece deadPiece) { Piece movingPiece = pieceAt(move.getFinish()); pieceComes(movingPiece, move.getStart()); @@ -85,16 +97,29 @@ public class ChessBoard { movingPiece.unMove(); } + /** + * Check if the cell is empty + * @param coordinate + * @return true if the cell is empty + */ public boolean isCellEmpty(Coordinate coordinate) { return pieceAt(coordinate) == null; } + /** + * Return the piece at the given coordinates + * @param coordinate + * @return piece at the given coordinates, or null if the coordinates are invalid or the cell is empty + */ public Piece pieceAt(Coordinate coordinate) { if (!coordinate.isValid()) return null; return cellAt(coordinate).getPiece(); } + /** + * Nuke all pieces of the board + */ public void clearBoard() { for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { @@ -103,24 +128,48 @@ public class ChessBoard { } } + /** + * Get the cell at the given coordinates + * @param coordinate + * @return + */ private Cell cellAt(Coordinate coordinate) { return this.cells[coordinate.getX()][coordinate.getY()]; } + + /* + * Set the piece at the given coordinate + */ public void pieceComes(Piece piece, Coordinate coordinate) { cellAt(coordinate).setPiece(piece); if (piece instanceof King) this.kingPos[piece.getColor().ordinal()] = coordinate; } + /** + * Remove the piece at the given coordiinates + * @param coordinate + */ public void pieceLeaves(Coordinate coordinate) { cellAt(coordinate).setPiece(null); } + /** + * Find the king of the given color on the board + * @param color + * @return the coordinates of the king + */ public Coordinate findKing(Color color) { return kingPos[color.ordinal()]; } + /** + * Get the allowed starting positions for a piece to attack the given Coordinate. Useful for PGN trasnlation. + * @param finish + * @param color + * @return coordinates of the pieces that could attack + */ public List getAllowedStarts(Coordinate finish, Color color) { List starts = new ArrayList<>(); for (int i = 0; i < Coordinate.VALUE_MAX; i++) { @@ -145,6 +194,11 @@ public class ChessBoard { return starts; } + /** + * Check if the king of the given color is in check + * @param color + * @return true if the king is in check + */ public boolean isKingInCheck(Color color) { Coordinate kingPos = findKing(color); assert kingPos.isValid() : "King position is invalid!"; @@ -164,10 +218,20 @@ public class ChessBoard { return false; } + /** + * Check if the given player has allowed moves + * @param player + * @return true if the player can move + */ public boolean hasAllowedMoves(Color player) { return !getAllowedMoves(player).isEmpty(); } + /** + * Get the allowed moves for the given player + * @param player + * @return + */ public List getAllowedMoves(Color player) { if (this.cachedAllowedMoves != null) { return this.cachedAllowedMoves; @@ -211,7 +275,10 @@ public class ChessBoard { return result; } - public List getAllowedMoves(Coordinate pieceCoords) { + /** + * Get all the end positions possible of a piece + */ + public List getPieceAllowedMoves(Coordinate pieceCoords) { Piece piece = pieceAt(pieceCoords); if (piece == null) return null; @@ -239,6 +306,13 @@ public class ChessBoard { return result; } + /** + * Check if the given player can castle with the given rook + * @param color + * @param rookX + * @param kingDirection + * @return true if the player can Castle + */ private boolean canCastle(Color color, int rookX, Direction kingDirection) { if (isKingInCheck(color)) return false; @@ -273,20 +347,32 @@ public class ChessBoard { return obstacle == null; } + /** + * @return wether the player can perform a kingside castling. + */ public boolean canSmallCastle(Color color) { return canCastle(color, 7, Direction.Right); } + /** + * Check if the given player is allowed to execute a queenside castling. + * @param color + * @return + */ public boolean canBigCastle(Color color) { return canCastle(color, 0, Direction.Left); } + /** + * Check if there is a pawn to be promoted by the given player. + * @return + */ public boolean pawnShouldBePromoted() { return pawnPromotePosition() != null; } /** - * + * Check if there's a pawn in the adversary line on the board * @return Null if there is no pawn to promote */ public Coordinate pawnPromotePosition() { @@ -297,7 +383,7 @@ public class ChessBoard { } /** - * + * Check if there's a pawn of the given player in the adversary line on the board * @return Null if there is no pawn to promote */ private Coordinate pawnPromotePosition(Color color) { @@ -315,6 +401,10 @@ public class ChessBoard { return null; } + /** + * Hash according to the pieces position on the board + * @return hash code + */ @Override public int hashCode() { int result = 0; @@ -329,10 +419,16 @@ public class ChessBoard { return result; } + /** + * @return the last played move + */ public Move getLastMove() { return this.lastMove; } + /** + * Updates the last move of the board and invalidate the allowedMoves cache. + */ public void setLastMove(Move lastMove) { this.lastMove = lastMove; this.cachedAllowedMoves = null; diff --git a/app/src/main/java/chess/model/Game.java b/app/src/main/java/chess/model/Game.java index da0fd1d..59015c3 100644 --- a/app/src/main/java/chess/model/Game.java +++ b/app/src/main/java/chess/model/Game.java @@ -38,19 +38,24 @@ public class Game { return playerTurn; } + /** + * Reset the game + */ public void reset() { resetPlayerTurn(); this.traitsPos.clear(); } + /** + * Reset the player turn. + * Aka: white for the win + */ public void resetPlayerTurn() { this.playerTurn = Color.White; } /** - * - * @param color the current player turn - * @return true if a draw should be declared + * Save the current board configuration */ public void saveTraitPiecesPos() { int piecesHash = this.board.hashCode(); @@ -67,14 +72,16 @@ public class Game { } /** - * - * @return true if a draw should occur + * Switch player turn */ public void switchPlayerTurn() { playerTurn = Color.getEnemy(playerTurn); } - // this is the bottleneck of algorithms using this chess engine + /** + * Check the status of the game ofr the specified player + * @return a GameStatus enum + */ public GameStatus checkGameStatus(Color color) { if (checkDraw()) return GameStatus.Draw; @@ -91,20 +98,32 @@ public class Game { return GameStatus.OnGoing; } + /** + * Check the status of the gamere + */ public GameStatus checkGameStatus() { return checkGameStatus(Color.getEnemy(getPlayerTurn())); } + /** + * Add a player move (basic move, castling, ...) to the game history. + */ public void addAction(PlayerCommand command) { this.movesHistory.add(command); } + /** + * @return the last player action + */ public PlayerCommand getLastAction() { if (this.movesHistory.isEmpty()) return null; return this.movesHistory.pop(); } + /** + * Update the last board move ater an undo. + */ public void updateLastMove() { if (this.movesHistory.isEmpty()) return; @@ -115,6 +134,9 @@ public class Game { } } + /** + * Remove the board configuration occurence from the game history + */ public void undoTraitPiecesPos() { int piecesHash = this.board.hashCode(); Integer count = this.traitsPos.get(piecesHash); @@ -126,6 +148,9 @@ public class Game { return this.movesHistory; } + /** + * @return wether a pawn should be promoted + */ public boolean pawnShouldBePromoted() { return this.board.pawnShouldBePromoted(); } diff --git a/app/src/main/java/chess/model/Move.java b/app/src/main/java/chess/model/Move.java index 67c7322..9e58359 100644 --- a/app/src/main/java/chess/model/Move.java +++ b/app/src/main/java/chess/model/Move.java @@ -15,6 +15,10 @@ public class Move { this.deadPieceCoords = finish; } + /** + * + * @return true if the move is valid, false otherwise + */ public boolean isValid() { return this.start.isValid() && this.finish.isValid() && !this.start.equals(this.finish); } @@ -27,6 +31,10 @@ public class Move { return finish; } + /** + * Returns the number of cells traversed by the move. + * @return int + */ public int traversedCells() { assert isValid() : "Move is invalid!"; @@ -45,6 +53,9 @@ public class Move { return 0; } + /** + * @return the coordinates of the cell in the middle of the move + */ public Coordinate getMiddle() { return Coordinate.fromIndex((getStart().toIndex() + getFinish().toIndex()) / 2); } diff --git a/app/src/main/java/chess/pgn/PgnExport.java b/app/src/main/java/chess/pgn/PgnExport.java index 29a0591..2022731 100644 --- a/app/src/main/java/chess/pgn/PgnExport.java +++ b/app/src/main/java/chess/pgn/PgnExport.java @@ -25,27 +25,6 @@ import chess.model.pieces.Pawn; public class PgnExport { - // public static void main(String[] args) { - // final Game game = new Game(); - // final CommandExecutor commandExecutor = new CommandExecutor(game); - - // DumbAI ai1 = new DumbAI(commandExecutor, Color.White); - // commandExecutor.addListener(ai1); - - // DumbAI ai2 = new DumbAI(commandExecutor, Color.Black); - // commandExecutor.addListener(ai2); - - // commandExecutor.addListener(new GameAdapter() { - // @Override - // public void onGameEnd() { - // System.out.println(exportGame(game)); - // commandExecutor.close(); - // } - // }); - - // commandExecutor.executeCommand(new NewGameCommand()); - // } - private static final PiecePgnName piecePgnName = new PiecePgnName(); private static Piece pieceAt(CommandExecutor commandExecutor, Coordinate coordinate) { @@ -76,6 +55,12 @@ public class PgnExport { } } + /** + * Resolve PGN ambiguity in the case of two pieces of the same type able to move on the same square. + * @param cmdExec the command executor attached to the game + * @param pieceMove move of the piece + * @return the character that solves the eventual ambiguity, empty if no ambiguity to start with + */ private static String resolveAmbiguity(CommandExecutor cmdExec, Move pieceMove) { Piece movingPiece = pieceAt(cmdExec, pieceMove.getStart()); @@ -109,6 +94,12 @@ public class PgnExport { return ""; } + /** + * From a move, get the capture-part of the associated PGN string. + * @param move the move + * @param movingPiece the piece that is moving + * @return the capture string of the PGN move + */ private static String capture(MoveCommand move, Piece movingPiece) { String result = ""; if (move.getDeadPiece() != null) { diff --git a/app/src/main/java/chess/pgn/PgnImport.java b/app/src/main/java/chess/pgn/PgnImport.java index 2462b3b..45414b6 100644 --- a/app/src/main/java/chess/pgn/PgnImport.java +++ b/app/src/main/java/chess/pgn/PgnImport.java @@ -50,6 +50,13 @@ public class PgnImport { return getMoves(parts[parts.length - 1]); } + private static final int COORDINATE_ANY = -1; + + /** + * Parse the moves from a PGN string. + * @param unparsedMoves + * @return + */ private static List getMoves(String unparsedMoves) { String[] moves = unparsedMoves.replaceAll("\\{.*?\\}", "") // Remove comments .replaceAll("\\n", " ") // Remove new lines @@ -79,6 +86,12 @@ public class PgnImport { return instructions; } + /** + * Parse a move from the PGN format and plays it in the given game. + * @param move + * @param game + * @return + */ private static List parseMove(String move, Game game) { if (move.equals("O-O-O")) return Arrays.asList(new CastlingCommand(true)); @@ -104,7 +117,7 @@ public class PgnImport { assert move.length() == 3 || move.length() == 2; - Coordinate ambiguity = new Coordinate(-1, -1); + Coordinate ambiguity = new Coordinate(COORDINATE_ANY, COORDINATE_ANY); // ambiguity if (move.length() == 3) { @@ -124,8 +137,17 @@ public class PgnImport { return cmds; } - private static Coordinate getStartCoord(Coordinate dest, Class pieceType, Coordinate ambiguity, - Game game) { + /** + * Get the start coordinate of a piece. + * @param dest the end position of the moving piece + * @param pieceType the type of the piece + * @param ambiguity coordinates of the moving piece, indicated with the constant COORDINATE_ANY. + * @param game the game + * @see COORDINATE_ANY + * @see getAmbiguityPattern + * @return the start coordinate of the piece + */ + private static Coordinate getStartCoord(Coordinate dest, Class pieceType, Coordinate ambiguity, Game game) { final ChessBoard board = game.getBoard(); List starts = board.getAllowedStarts(dest, game.getPlayerTurn()); @@ -152,10 +174,10 @@ public class PgnImport { } private static boolean coordPatternMatch(Coordinate coord, Coordinate pattern) { - if (pattern.getX() != -1 && coord.getX() != pattern.getX()) + if (pattern.getX() != COORDINATE_ANY && coord.getX() != pattern.getX()) return false; - if (pattern.getY() != -1 && coord.getY() != pattern.getY()) + if (pattern.getY() != COORDINATE_ANY && coord.getY() != pattern.getY()) return false; return true; @@ -163,8 +185,8 @@ public class PgnImport { private static Coordinate getAmbiguityPattern(char amb) { if (Character.isDigit(amb)) - return new Coordinate(-1, getYCoord(amb)); - return new Coordinate(getXCoord(amb), -1); + return new Coordinate(COORDINATE_ANY, getYCoord(amb)); + return new Coordinate(getXCoord(amb), COORDINATE_ANY); } private static Coordinate stringToCoordinate(String coordinates) { diff --git a/app/src/main/java/chess/view/DDDrender/Camera.java b/app/src/main/java/chess/view/DDDrender/Camera.java index 0dfe6b8..cb1cb30 100644 --- a/app/src/main/java/chess/view/DDDrender/Camera.java +++ b/app/src/main/java/chess/view/DDDrender/Camera.java @@ -71,6 +71,7 @@ public class Camera { /** * Performs a ray casting to find the selected cell by the cursor + * @return the position of the cell in world coordinates */ public Vector2f getCursorWorldFloorPos(Vector2f screenPos, int windowWidth, int windowHeight) { float relativeX = (screenPos.x / (float) windowWidth * 2.0f) - 1.0f; diff --git a/app/src/main/java/chess/view/DDDrender/DDDView.java b/app/src/main/java/chess/view/DDDrender/DDDView.java index ff31fc6..cc327b3 100644 --- a/app/src/main/java/chess/view/DDDrender/DDDView.java +++ b/app/src/main/java/chess/view/DDDrender/DDDView.java @@ -68,7 +68,10 @@ public class DDDView extends GameAdapter implements CommandSender { this.click = coordinate; } - // Invoked when a cell is clicked + /** + * Invoked when a cell is clicked. The first click selects the piece to move, the second click selects the destination. + * @param coordinate + */ private void onCellClick(Coordinate coordinate) { if (this.click == null) { // case: first click List allowedMoves = getPieceAllowedMoves(coordinate); @@ -107,6 +110,10 @@ public class DDDView extends GameAdapter implements CommandSender { cancelClick(); // case: cancelling previous click } + /** + * Show the possible moves of the piece at the given coordinates: the piece is highlighted in yellow, the allowed moves in red. + * @param coordinate + */ private void previewMoves(Coordinate coordinate) { List allowedMoves = getPieceAllowedMoves(coordinate); if (allowedMoves.isEmpty()) @@ -118,7 +125,10 @@ public class DDDView extends GameAdapter implements CommandSender { } } - // Invoked when a cell is hovered + /** + * Invoked when a cell is hovered. The hovered cell is highlighted in red. If the cell contains a piece, its allowed moves are highlighted in red as well. + * @param coordinate + */ private void onCellEnter(Coordinate coordinate) { if (this.click == null) { // small test turning a cell red when hovered @@ -138,7 +148,10 @@ public class DDDView extends GameAdapter implements CommandSender { } } - // Invoked when a cell is not hovered anymore + /** + * Invoked when a cell is not hovered anymore, cancel that cell's preview. + * @param coordinate + */ private void onCellExit(Coordinate coordinate) { if (this.click == null) { this.boardEntity.resetCellColor(coordinate); @@ -160,6 +173,10 @@ public class DDDView extends GameAdapter implements CommandSender { } } + /** + * Cancel the preview of the moves for the given coordinates. + * @param coordinate + */ private void cancelPreview(Coordinate coordinate) { this.boardEntity.resetCellColor(coordinate); Piece p = getPieceAt(coordinate); @@ -174,6 +191,9 @@ public class DDDView extends GameAdapter implements CommandSender { } } + /** + * Start the game by initializing the board, creating the events, and starting the game loop. + */ @Override public void onGameStart() { this.window.scheduleTask(() -> { @@ -208,6 +228,9 @@ public class DDDView extends GameAdapter implements CommandSender { ImGui.text("FPS : " + (int) ImGui.getIO().getFramerate()); } + /** + * Render the footer of the window. + */ private void onFooterRender() { ImGui.beginDisabled(!canDoCastling()); if (ImGui.button("Roque")) { @@ -235,6 +258,13 @@ public class DDDView extends GameAdapter implements CommandSender { } } + /** + * Create the 3D piece at the given coordinates + * @param piece the piece to create + * @param coordinate the coordinates of the piece + * @return the piece entity + * @throws IOException + */ private PieceEntity createDefault(Piece piece, Coordinate coordinate) throws IOException { Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(coordinate); Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); @@ -244,6 +274,10 @@ public class DDDView extends GameAdapter implements CommandSender { piece.getColor() == Color.White ? 0.0f : (float) Math.PI); } + /** + * Create the 3D board and add the pieces. + * @throws IOException + */ private void initBoard() throws IOException { for (int i = 0; i < Coordinate.VALUE_MAX; i++) { for (int j = 0; j < Coordinate.VALUE_MAX; j++) { @@ -260,6 +294,7 @@ public class DDDView extends GameAdapter implements CommandSender { } /** + * Calculate the position of the point at the t position on a Bezier curve spanning from the begin point to the end point and goign through the control point. * @param begin begin * @param middle control point * @param end end @@ -270,8 +305,14 @@ public class DDDView extends GameAdapter implements CommandSender { return begin.mul((1.0f - t) * (1.0f - t)).add(middle.mul(2.0f * t * (1.0f - t))).add(end.mul(t * t)); } + /** + * Move the piece on the board according to the given move. Called in a loop to animate the movement. + * @param progress the proportion of the animation already done + * @param piece + * @param move + */ private void pieceTick(float progress, PieceEntity piece, Move move) { - float height = 1; // how much the piece is raised + float height = 1; // how high the piece is raised Vector2f pieceStartBoard = DDDPlacement.coordinatesToVector(move.getStart()); Vector2f pieceDestinationBoard = DDDPlacement.coordinatesToVector(move.getFinish()); Vector3f start = new Vector3f(pieceStartBoard.x(), 0, pieceStartBoard.y()); @@ -282,6 +323,10 @@ public class DDDView extends GameAdapter implements CommandSender { piece.setPosition(bezierCurve(start, top, end, progress)); } + /** + * Move the pieces on the board according to the given moves. + * @param moves + */ private void move3DPieces(List moves) { final List pEntities = new ArrayList<>(moves.size()); final List> consumers = new ArrayList<>(moves.size()); @@ -333,12 +378,19 @@ public class DDDView extends GameAdapter implements CommandSender { move3DPieces(List.of(move)); } + /** + * Rotate the camera. Calles in a loop to animate the rotation. + * @param delta the proportion of the animation already done + */ private void cameraTick(float delta) { int oddAnimationTurn = (2 * (animationTurns - 1)) + 1; final float angle = (float) Math.PI; this.camera.setRotateAngle(this.camera.getRotateAngle() + angle * delta * oddAnimationTurn / animationTime); } + /** + * Rotate the camera so the current player faces his enemy's pieces. + */ private void cameraRotate() { float end = this.camera.getRotateAngle() + (float) Math.PI; Consumer rotationConsumer = this::cameraTick; @@ -357,6 +409,9 @@ public class DDDView extends GameAdapter implements CommandSender { cameraRotate(); } + /** + * Run the game. + */ public void run() { this.window.run(); @@ -369,6 +424,11 @@ public class DDDView extends GameAdapter implements CommandSender { } } + /** + * Render a popup with the given title and content. + * @param title + * @param content + */ private void renderPopup(String title, Runnable content) { ImVec2 center = ImGui.getMainViewport().getCenter(); ImGui.setNextWindowPos(center, ImGuiCond.Appearing, new ImVec2(0.5f, 0.5f)); @@ -392,6 +452,9 @@ public class DDDView extends GameAdapter implements CommandSender { renderPopup(title, () -> ImGui.text(text)); } + /** + * Open the promotion dialog. + */ private void renderPromoteDialog() { renderPopup("Promotion", () -> { ImGui.text("Select the promotion type :"); @@ -406,6 +469,9 @@ public class DDDView extends GameAdapter implements CommandSender { }); } + /** + * List of possible popups. + */ private void renderPopups() { renderPopup("Check", "Your king is in check"); renderPopup("Checkmate", "Checkmate, it's a win!"); @@ -419,6 +485,10 @@ public class DDDView extends GameAdapter implements CommandSender { renderPopup("End", "End of the game, thank you for playing!"); } + /** + * Open the popup identified by the given title and block the current thread until the popup is closed. + * @param title the title of the popup to open + */ private void openPopup(String title) { this.waitingPopup = title; // block the current thread until the popup is closed @@ -477,6 +547,9 @@ public class DDDView extends GameAdapter implements CommandSender { openPopup("Promotion"); } + /** + * Update the view with the promoted piece + */ @Override public void onPawnPromoted(PromoteType promotion, Coordinate coordinate) { this.window.scheduleTask(() -> { diff --git a/app/src/main/java/chess/view/DDDrender/Window.java b/app/src/main/java/chess/view/DDDrender/Window.java index 931a893..e662474 100644 --- a/app/src/main/java/chess/view/DDDrender/Window.java +++ b/app/src/main/java/chess/view/DDDrender/Window.java @@ -43,7 +43,7 @@ import static org.lwjgl.system.MemoryUtil.*; public class Window implements Closeable { // The window handle - private long window; + private long window; private final ImGuiImplGl3 implGl3 = new ImGuiImplGl3(); private final ImGuiImplGlfw implGlfw = new ImGuiImplGlfw(); @@ -77,20 +77,39 @@ public class Window implements Closeable { this.regularTasks = new ArrayList<>(); } + /** + * Add a task to be executed in the next frames. + * @param task + */ public synchronized void addRegularTask(Consumer task) { this.regularTasks.add(task); } + /** + * Remove a task that had been set to be executed in the next frames. + * @param task + */ public synchronized void removeRegularTask(Consumer task) {this.regularTasks.remove(task);} + /** + * Schedule a task to be executed in the next frame (3d thread). + * @param runnable + */ public synchronized void scheduleTask(Runnable runnable) { this.tasks.add(runnable); } - public synchronized Runnable getNextTask() { + /** + * Get the next task to be executed. + * @return + */ + private synchronized Runnable getNextTask() { return this.tasks.poll(); } + /** + * Run the window. + */ public void run() { System.out.println("LWJGL " + Version.getVersion() + "!"); @@ -111,6 +130,9 @@ public class Window implements Closeable { ImGui.destroyContext(); } + /** + * Set the configuration of ImGui + */ private void initImGui() { ImGui.setCurrentContext(ImGui.createContext()); @@ -123,11 +145,13 @@ public class Window implements Closeable { ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("fonts/comic.ttf").readAllBytes(), 50.0f, config); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } + /** + * Initialize the window. + */ private void init() { // Setup an error callback. The default implementation // will print the error message in System.err. @@ -200,6 +224,10 @@ public class Window implements Closeable { } } + /** + * Execute the tasks in the queue. + * @param delta time since the last frame + */ private synchronized void executeTasks(float delta) { Runnable task = getNextTask(); while (task != null) { @@ -211,6 +239,13 @@ public class Window implements Closeable { } } + /** + * Check the cursor position to identify the cell under it. + * @param cursorPosX position of cursor on the x axis + * @param cursorPosY position of cursor on the y axis + * @param windowWidth width of the window + * @param windowHeight height of the window + */ private void checkCursor(float cursorPosX, float cursorPosY, int windowWidth, int windowHeight) { Vector2f cursorPos = this.cam.getCursorWorldFloorPos(new Vector2f(cursorPosX, cursorPosY), windowWidth, windowHeight); @@ -266,6 +301,9 @@ public class Window implements Closeable { ImGui.end(); } + /** + * Keep on rendering until the user close the window. + */ private void loop() { // Set the clear color @@ -309,6 +347,9 @@ public class Window implements Closeable { } } + /** + * Closes the window + */ public void stop() { shouldBeClosed = true; } diff --git a/app/src/main/java/chess/view/DDDrender/loader/BoardModelLoader.java b/app/src/main/java/chess/view/DDDrender/loader/BoardModelLoader.java index af5f027..2cff7a8 100644 --- a/app/src/main/java/chess/view/DDDrender/loader/BoardModelLoader.java +++ b/app/src/main/java/chess/view/DDDrender/loader/BoardModelLoader.java @@ -16,6 +16,11 @@ public class BoardModelLoader { private static final Vector3f WHITE = new Vector3f(1, 1, 1); private static final Vector3f BLACK = new Vector3f(0, 0, 0); + /** + * Create the points to make a 3D board. + * Note of developers: "Trust me on this one, bro". + * @return array of 3D points, corressponding to the cells composing the board + */ private static float[] GetBoardPositions() { float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3]; for (int i = 0; i < BOARD_WIDTH; i++) { @@ -50,6 +55,11 @@ public class BoardModelLoader { return positions; } + /** + * Assign each cell of the bopard to a color. + * Note of developers: "Why are you even reading this ?" + * @return array of colors, corresponding to the cells composing the board + */ private static float[] GetBoardColors() { float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3]; for (int i = 0; i < BOARD_WIDTH; i++) { @@ -66,6 +76,11 @@ public class BoardModelLoader { return colors; } + /** + * Return which points compose each cell. + * Note of developers: "You should stop before you have an heart attack" + * @return array of indicies, corresponding to the cells composing the board + */ private static int[] GetBoardIndicies() { int[] indices = new int[BOARD_SIZE * 6]; for (int i = 0; i < BOARD_SIZE; i++) { @@ -79,6 +94,11 @@ public class BoardModelLoader { return indices; } + /** + * Create an OpenGL VertexArrayObject (VAO) using all of the functions above for data. + * Note of developers : "Stop annoying me and go read the doc of OpenGL yourself. Please?" + * @return the VAO + */ public static VertexArray GetBoardModel() { ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies()); VertexArray vao = new VertexArray(eBuffer); diff --git a/app/src/main/java/chess/view/DDDrender/loader/ModelLoader.java b/app/src/main/java/chess/view/DDDrender/loader/ModelLoader.java index 24e2d82..fc0aeab 100644 --- a/app/src/main/java/chess/view/DDDrender/loader/ModelLoader.java +++ b/app/src/main/java/chess/view/DDDrender/loader/ModelLoader.java @@ -27,6 +27,9 @@ public class ModelLoader { private static final int VERTEX_POSITION_INDEX = 0; private static final int VERTEX_NORMAL_INDEX = 1; + /** + * Note of developers: "Kill me please" + */ private static float[] toFloatArray(List list) { float[] result = new float[list.size()]; for (int i = 0; i < list.size(); i++) { @@ -35,6 +38,9 @@ public class ModelLoader { return result; } + /** + * Note of developers : "I'd rather write assembly code than watch this again" + */ private static int[] toIntArray(List list) { int[] result = new int[list.size()]; for (int i = 0; i < list.size(); i++) { @@ -43,6 +49,12 @@ public class ModelLoader { return result; } + /** + * Process a mesh and create a vertex array object from it. + * @param mesh the mesh to process + * @param scene the model scene + * @return + */ private static VertexArray processMesh(AIMesh mesh, AIScene scene) { List positions = new ArrayList<>(); List normals = new ArrayList<>(); @@ -90,6 +102,12 @@ public class ModelLoader { } + /** + * Recursively process a node and its children. + * @param node + * @param scene + * @param meshes + */ private static void processNode(AINode node, AIScene scene, List meshes) { for (int i = 0; i < node.mNumChildren(); i++) { AINode child = AINode.create(node.mChildren().get(i)); @@ -101,6 +119,9 @@ public class ModelLoader { } } + /** + * Load a 3D model from a file (usually .fbx) + */ public static DDDModel loadModel(String filename) throws IOException { InputStream input = AssetManager.getResource(filename); byte[] buffer = input.readAllBytes(); diff --git a/app/src/main/java/chess/view/DDDrender/shader/ShaderProgram.java b/app/src/main/java/chess/view/DDDrender/shader/ShaderProgram.java index a9d142a..a1b5332 100644 --- a/app/src/main/java/chess/view/DDDrender/shader/ShaderProgram.java +++ b/app/src/main/java/chess/view/DDDrender/shader/ShaderProgram.java @@ -30,6 +30,11 @@ public abstract class ShaderProgram implements Closeable { GL30.glUseProgram(0); } + /** + * Load the shader program. + * @param vertexSource the source code of the vertex shader. + * @param fragmentSource the source code of the fragment shader. + */ public void LoadProgram(String vertexSource, String fragmentSource) { this.vertexShaderId = LoadShader(vertexSource, GL30.GL_VERTEX_SHADER); this.fragmentShaderId = LoadShader(fragmentSource, GL30.GL_FRAGMENT_SHADER); @@ -47,6 +52,12 @@ public abstract class ShaderProgram implements Closeable { GetAllUniformLocation(); } + /** + * Load a shader from source code. + * @param source + * @param type + * @return + */ private int LoadShader(String source, int type) { int shaderId = GL30.glCreateShader(type); diff --git a/app/src/main/java/chess/view/DDDrender/world/BoardEntity.java b/app/src/main/java/chess/view/DDDrender/world/BoardEntity.java index 871bf81..5ea2a1a 100644 --- a/app/src/main/java/chess/view/DDDrender/world/BoardEntity.java +++ b/app/src/main/java/chess/view/DDDrender/world/BoardEntity.java @@ -27,6 +27,11 @@ public class BoardEntity extends Entity { this.colorVbo = this.vao.getVertexBuffers().get(1); } + /** + * Set the color of a cell. + * @param coord the coordinate of the cell. + * @param color the color of the cell in RGB format. + */ public void setCellColor(Coordinate coord, Vector3f color) { float[] data = { color.x, color.y, color.z, color.x, color.y, color.z, color.x, color.y, color.z, color.x, color.y, color.z}; diff --git a/app/src/main/java/chess/view/audio/AudioPlayer.java b/app/src/main/java/chess/view/audio/AudioPlayer.java index 4954855..7cf16a2 100644 --- a/app/src/main/java/chess/view/audio/AudioPlayer.java +++ b/app/src/main/java/chess/view/audio/AudioPlayer.java @@ -12,6 +12,11 @@ import javax.sound.sampled.Clip; * Audio player class to play sound files. */ public class AudioPlayer { + + /** + * Play a sound file. + * @param audio the audio input stream to play + */ public static void playSound(InputStream audio) { new Thread(new Runnable() { // The wrapper thread is unnecessary, unless it blocks on the @@ -30,6 +35,11 @@ public class AudioPlayer { }).start(); } + /** + * Convert the given audio input stream to PCM format. + * @param audioInputStream the audio input stream to convert + * @return + */ private static AudioInputStream convertToPCM(AudioInputStream audioInputStream) { AudioFormat m_format = audioInputStream.getFormat(); diff --git a/app/src/main/java/chess/view/consolerender/Console.java b/app/src/main/java/chess/view/consolerender/Console.java index af6d587..4858d00 100644 --- a/app/src/main/java/chess/view/consolerender/Console.java +++ b/app/src/main/java/chess/view/consolerender/Console.java @@ -38,6 +38,12 @@ public class Console extends GameAdapter implements CommandSender { this(commandExecutor, true); } + /** + * Translate a string containing chess coordinates (such as "a1" or "d6") to coordinates. + * @param coordinates the string to translate + * @return the coordinates of the cell + * @throws Exception if the string is not valid + */ public Coordinate stringToCoordinate(String coordinates) throws Exception { char xPos = coordinates.charAt(0); char yPos = coordinates.charAt(1); @@ -56,6 +62,9 @@ public class Console extends GameAdapter implements CommandSender { return new Coordinate(x, y); } + /** + * Open a dialog so the user can, during their turn, move a piece, show move previews, or surrender. + */ @Override public void onPlayerTurn(Color color, boolean undone) { if (!captureInput) @@ -90,6 +99,11 @@ public class Console extends GameAdapter implements CommandSender { return true; } + /** + * Ask the user to pick a move + * @param color the color of the player + * @return true if there has been a move, false otherwise + */ public boolean playerPickedMove(Color color) { try { System.out.println("Piece to move, or \"castling\" for a castling"); @@ -113,6 +127,11 @@ public class Console extends GameAdapter implements CommandSender { } } + /** + * Ask the user to pick a piece, and show its potential moves + * @param color + * @return + */ private boolean playerPickedShowMoves(Color color) { try { System.out.println("Piece to examine: "); @@ -168,6 +187,10 @@ public class Console extends GameAdapter implements CommandSender { this.executor.shutdown(); } + /** + * Open the dialog to promote a pawn. + * @param pieceCoords the coordinates of the pawn to promote + */ @Override public void onPromotePawn(Coordinate pieceCoords) { System.out.println("The pawn on the " + pieceCoords + " coordinates needs to be promoted."); @@ -195,6 +218,9 @@ public class Console extends GameAdapter implements CommandSender { }); } + /** + * Update and print the board in the console. + */ @Override public void onBoardUpdate() { if (!this.captureInput) @@ -222,6 +248,11 @@ public class Console extends GameAdapter implements CommandSender { System.out.flush(); } + /** + * Display the possible moves of a piece. + * @param piece + * @param moves + */ public void displayMoves(Coordinate piece, List moves) { StringBuilder string = new StringBuilder(); string.append(" a b c d e f g h \n"); @@ -265,6 +296,10 @@ public class Console extends GameAdapter implements CommandSender { System.out.println("Repeated positions!"); } + /** + * Open different dialogs according to which castling is allowed. + * @return true if a castling was played, false otherwise + */ private boolean onAskedCastling() { return switch (getAllowedCastlings()) { case Small -> onSmallCastling(); @@ -277,6 +312,10 @@ public class Console extends GameAdapter implements CommandSender { }; } + /** + * Ask the user to confirm a small castling. + * @return true if the castling was played, false otherwise + */ private boolean onSmallCastling() { System.out.println("Small castling allowed. Confirm with \"y\":"); String answer = scanner.nextLine(); @@ -287,6 +326,10 @@ public class Console extends GameAdapter implements CommandSender { } } + /** + * Ask the user to confirm a big castling. + * @return true if the castling was played, false otherwise + */ private boolean onBigCastling() { System.out.println("Big castling allowed. Confirm with \"y\":"); String answer = scanner.nextLine(); @@ -297,6 +340,10 @@ public class Console extends GameAdapter implements CommandSender { } } + /** + * Ask the user to pick a castling when both are allowed. + * @return true if a castling was played, false otherwise + */ private boolean onBothCastling() { System.out.println("Both castlings allowed. Pick \"s\" to play a castling, \"b\" to play a big castling."); String answer = scanner.nextLine(); diff --git a/app/src/main/java/chess/view/simplerender/PieceIcon.java b/app/src/main/java/chess/view/simplerender/PieceIcon.java index 58fc768..2ad50bd 100644 --- a/app/src/main/java/chess/view/simplerender/PieceIcon.java +++ b/app/src/main/java/chess/view/simplerender/PieceIcon.java @@ -28,6 +28,12 @@ public class PieceIcon implements PieceVisitor { private static final String basePath = "pieces2D/"; private static final Map cache = new HashMap<>(); + /** + * Get icon of the given piece. + * @param piece + * @return icon of the piece + * @throws IOException + */ public Icon getIcon(Piece piece) throws IOException { if (piece == null) return null; @@ -35,6 +41,12 @@ public class PieceIcon implements PieceVisitor { return getIcon(path); } + /** + * Get icon at the given path. + * @param path + * @return icon at the given path + * @throws IOException + */ private Icon getIcon(String path) throws IOException { Icon image = cache.get(path); if (image != null) diff --git a/app/src/main/java/chess/view/simplerender/Window.java b/app/src/main/java/chess/view/simplerender/Window.java index 2cfd54a..2503cad 100644 --- a/app/src/main/java/chess/view/simplerender/Window.java +++ b/app/src/main/java/chess/view/simplerender/Window.java @@ -62,6 +62,10 @@ public class Window extends JFrame implements GameListener, CommandSender { return ((x + y) % 2 == 1) ? Color.DARK_GRAY : Color.LIGHT_GRAY; } + /** + * Build and set the buttons of the window. + * @param bottom the Jpanel in which to add the buttons + */ @SuppressWarnings("unused") private void buildButtons(JPanel bottom) { castlingButton.addActionListener((event) -> { @@ -81,6 +85,9 @@ public class Window extends JFrame implements GameListener, CommandSender { bottom.add(undoButton); } + /** + * Build the playable board in the window. + */ private void buildBoard() { JPanel content = new JPanel(); JPanel grid = new JPanel(new GridLayout(8, 8)); @@ -117,6 +124,9 @@ public class Window extends JFrame implements GameListener, CommandSender { updateBoard(); } + /** + * Update the board with the newest pieces' positions. + */ private void updateBoard() { PieceIcon pieceIcon = new PieceIcon(); for (int y = 0; y < 8; y++) { @@ -131,6 +141,12 @@ public class Window extends JFrame implements GameListener, CommandSender { } } + /** + * Show the possible moves of the piece at the given coordinates + * @param x position of the piece on the x axis + * @param y position of the piece on the y axis + * @return true if the piece has possible moves, false otherwise + */ private boolean previewMoves(int x, int y) { List allowedMoves = getPieceAllowedMoves(new Coordinate(x, y)); if (allowedMoves.isEmpty()) @@ -162,6 +178,11 @@ public class Window extends JFrame implements GameListener, CommandSender { } } + /** + * Handle the click on a cell. Cancel previous rendering, move the previously selected piece if needed. + * @param x position of the cell on the x axis + * @param y position of the cell on the y axis + */ private void onCellClicked(int x, int y) { clearMoves(); if (this.lastClick == null) { @@ -233,6 +254,9 @@ public class Window extends JFrame implements GameListener, CommandSender { buildBoard(); } + /** + * Open a dialog box when a pawn needs to be promoted + */ @Override public void onPromotePawn(Coordinate pieceCoords) { if (!showPopups) diff --git a/app/src/main/java/common/AssetManager.java b/app/src/main/java/common/AssetManager.java index 11e427f..a6c8f59 100644 --- a/app/src/main/java/common/AssetManager.java +++ b/app/src/main/java/common/AssetManager.java @@ -14,6 +14,11 @@ public class AssetManager { private static final String gradleBase = "app/src/main/resources/"; + /** + * Get a resource from the classpath or from the file system. + * @param name the name of the resource + * @return the InputStream of the resource + */ public static InputStream getResource(String name) { // we first search it in files InputStream inputStream = getFileInputStream(name); @@ -34,6 +39,11 @@ public class AssetManager { return builder.toString(); } + /** + * Get a resource from the file system. + * @param path the path of the resource + * @return the InputStream of the resource + */ private static InputStream getFileInputStream(String path) { File f = new File(path); if (f.exists()) {