7 Commits

Author SHA1 Message Date
fa3c6672e4 ignore classes 2025-04-07 10:17:27 +02:00
b920c4fbb3 change board to cells 2025-04-07 10:17:19 +02:00
Janet-Doe
f4b5e10e5b addition of decorator to manage pieces' moves 2025-03-31 12:02:23 +02:00
Janet-Doe
747bc62596 contoller 2025-03-31 11:21:27 +02:00
Janet-Doe
d60e66fd09 working piece display + basic moves 2025-03-25 16:50:31 +01:00
Janet-Doe
c8c6019b15 addo pieces 2025-03-25 10:21:50 +01:00
Janet-Doe
d720e16de0 Implementation of 2D mode 2025-03-24 11:17:32 +01:00
109 changed files with 897 additions and 4794 deletions

4
.gitattributes vendored
View File

@@ -3,7 +3,7 @@
# #
# Linux start script should use lf # Linux start script should use lf
/gradlew text eol=lf /gradlew text eol=lf
# These are Windows script files and should use crlf # These are Windows script files and should use crlf
*.bat text eol=crlf *.bat text eol=crlf
*.glb filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored
View File

@@ -5,5 +5,3 @@
build build
app/bin app/bin
.vscode

View File

@@ -17,7 +17,7 @@ repositories {
} }
def lwjgl_version = "3.3.6" def lwjgl_version = "3.3.6"
def lwjgl_natives = "natives-linux" def lwjgl_natives = "natives-windows"
dependencies { dependencies {
// Use JUnit Jupiter for testing. // Use JUnit Jupiter for testing.
@@ -26,29 +26,16 @@ dependencies {
implementation "org.lwjgl:lwjgl:$lwjgl_version" implementation "org.lwjgl:lwjgl:$lwjgl_version"
implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version" implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version"
implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version" implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version"
implementation "org.lwjgl:lwjgl-assimp:$lwjgl_version"
implementation "org.joml:joml:1.10.8" implementation "org.joml:joml:1.10.8"
implementation "org.lwjgl:lwjgl::$lwjgl_natives" implementation "org.lwjgl:lwjgl::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives" implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives" implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-assimp::$lwjgl_natives"
} }
application { application {
// Define the main class for the application. // Define the main class for the application.
mainClass = "chess.App" mainClass = "chess.App"
applicationName = "3DChess"
}
jar {
manifest {
attributes 'Main-Class': application.mainClass
}
}
run {
standardInput = System.in
} }
tasks.named('test') { tasks.named('test') {

View File

@@ -1,30 +1,16 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package chess; package chess;
import java.util.Scanner; import chess.view.render2D.Window;
import chess.view.consolerender.Colors;
public class App { public class App {
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(Colors.RED + "Credits: Grenier Lilas, Pribylski Simon." + Colors.RESET); Window.main(args);
System.out.println("""
Pick the version to use:
1 - Console
2 - Window
3 - 3D.""");
switch (new Scanner(System.in).nextLine()) {
case "1", "Console", "console":
ConsoleMain.main(args);
break;
case "2", "Window", "window":
SwingMain.main(args);
break;
case "3", "3D", "3d":
OpenGLMain.main(args);
break;
default:
System.out.println("Invalid input");
break;
}
} }
} }

View File

@@ -1,21 +0,0 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package chess;
import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand;
import chess.model.Game;
import chess.view.consolerender.Console;
public class ConsoleMain {
public static void main(String[] args) {
Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game);
Console console = new Console(commandExecutor);
commandExecutor.addListener(console);
commandExecutor.executeCommand(new NewGameCommand());
}
}

View File

@@ -1,21 +0,0 @@
package chess;
import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand;
import chess.model.Game;
import chess.view.DDDrender.DDDView;
public class OpenGLMain {
public static void main(String[] args) {
Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game);
DDDView ddd = new DDDView(commandExecutor);
commandExecutor.addListener(ddd);
commandExecutor.executeCommand(new NewGameCommand());
ddd.run();
commandExecutor.close();
}
}

View File

@@ -1,36 +0,0 @@
package chess;
import chess.ai.DumbAI;
import chess.ai.HungryAI;
import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand;
import chess.controller.event.GameAdaptator;
import chess.model.Color;
import chess.model.Game;
import chess.pgn.PgnExport;
import chess.view.simplerender.Window;
public class SwingMain {
public static void main(String[] args) {
Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game);
Window window = new Window(commandExecutor, false);
commandExecutor.addListener(window);
DumbAI ai = new DumbAI(commandExecutor, Color.Black);
commandExecutor.addListener(ai);
HungryAI ai2 = new HungryAI(commandExecutor, Color.White);
commandExecutor.addListener(ai2);
commandExecutor.addListener(new GameAdaptator(){
@Override
public void onGameEnd() {
System.out.println(PgnExport.exportGame(game));
}
});
commandExecutor.executeCommand(new NewGameCommand());
}
}

View File

@@ -1,68 +0,0 @@
package chess.ai;
import java.util.List;
import chess.controller.Command;
import chess.controller.CommandExecutor;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.GetPlayerMovesCommand;
import chess.controller.commands.GetAllowedCastlingsCommand;
import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.event.GameAdaptator;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public abstract class AI extends GameAdaptator{
protected final CommandExecutor commandExecutor;
protected final Color color;
public AI(CommandExecutor commandExecutor, Color color) {
this.commandExecutor = commandExecutor;
this.color = color;
}
protected abstract void play();
protected abstract void promote(Coordinate pawnCoords);
@Override
public void onPlayerTurn(Color color, boolean undone) {
if (this.color != color || undone)
return;
play();
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
Piece pawn = pieceAt(pieceCoords);
if (pawn.getColor() != this.color)
return;
promote(pieceCoords);
}
protected Piece pieceAt(Coordinate coordinate) {
GetPieceAtCommand command = new GetPieceAtCommand(coordinate);
sendCommand(command);
return command.getPiece();
}
protected List<Move> getAllowedMoves() {
GetPlayerMovesCommand cmd = new GetPlayerMovesCommand();
sendCommand(cmd);
return cmd.getMoves();
}
protected CastlingResult getAllowedCastlings() {
GetAllowedCastlingsCommand cmd2 = new GetAllowedCastlingsCommand();
sendCommand(cmd2);
return cmd2.getCastlingResult();
}
protected void sendCommand(Command command) {
this.commandExecutor.executeCommand(command);
}
}

View File

@@ -1,61 +0,0 @@
package chess.ai;
import java.util.List;
import java.util.Random;
import chess.controller.CommandExecutor;
import chess.controller.commands.CastlingCommand;
import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.commands.PromoteCommand.PromoteType;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
public class DumbAI extends AI {
private final Random random = new Random();
public DumbAI(CommandExecutor commandExecutor, Color color) {
super(commandExecutor, color);
}
@Override
protected void play() {
CastlingResult castlings = getAllowedCastlings();
List<Move> moves = getAllowedMoves();
switch (castlings) {
case Both: {
int randomMove = this.random.nextInt(moves.size() + 2);
if (randomMove < moves.size() - 2)
break;
this.commandExecutor.executeCommand(new CastlingCommand(randomMove == moves.size()));
return;
}
case Small:
case Big: {
int randomMove = this.random.nextInt(moves.size() + 1);
if (randomMove != moves.size())
break;
this.commandExecutor.executeCommand(new CastlingCommand(castlings == CastlingResult.Big));
return;
}
default:
break;
}
int randomMove = this.random.nextInt(moves.size());
this.commandExecutor.executeCommand(new MoveCommand(moves.get(randomMove)));
}
@Override
protected void promote(Coordinate pawnCoords) {
int promote = this.random.nextInt(PromoteType.values().length);
this.commandExecutor.executeCommand(new PromoteCommand(PromoteType.values()[promote]));
}
}

View File

@@ -1,61 +0,0 @@
package chess.ai;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import chess.controller.CommandExecutor;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.commands.PromoteCommand.PromoteType;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public class HungryAI extends AI {
private final PieceCost pieceCost;
private final Random random;
public HungryAI(CommandExecutor commandExecutor, Color color) {
super(commandExecutor, color);
this.pieceCost = new PieceCost(color);
this.random = new Random();
}
private int getMoveCost(Move move) {
Piece piece = pieceAt(move.getDeadPieceCoords());
return pieceCost.getCost(piece);
}
private List<Move> getBestMoves() {
List<Move> moves = getAllowedMoves();
List<Move> bestMoves = new ArrayList<>();
int bestCost = 0;
for (Move move : moves) {
int moveCost = getMoveCost(move);
if (moveCost == bestCost) {
bestMoves.add(move);
} else if (moveCost > bestCost) {
bestMoves.clear();
bestMoves.add(move);
bestCost = moveCost;
}
}
return bestMoves;
}
@Override
protected void play() {
List<Move> bestMoves = getBestMoves();
int randomMove = this.random.nextInt(bestMoves.size());
this.commandExecutor.executeCommand(new MoveCommand(bestMoves.get(randomMove)));
}
@Override
protected void promote(Coordinate pawnCoords) {
sendCommand(new PromoteCommand(PromoteType.Queen));
}
}

View File

@@ -1,60 +0,0 @@
package chess.ai;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class PieceCost implements PieceVisitor<Integer> {
private final Color player;
public PieceCost(Color color) {
this.player = color;
}
public int getCost(Piece piece) {
if (piece == null)
return 0;
int cost = visit(piece);
if (piece.getColor() == player)
cost = -cost;
return cost;
}
@Override
public Integer visitPiece(Bishop bishop) {
return 3;
}
@Override
public Integer visitPiece(King king) {
return 90;
}
@Override
public Integer visitPiece(Knight knight) {
return 3;
}
@Override
public Integer visitPiece(Pawn pawn) {
return 1;
}
@Override
public Integer visitPiece(Queen queen) {
return 9;
}
@Override
public Integer visitPiece(Rook rook) {
return 5;
}
}

View File

@@ -1,23 +0,0 @@
package chess.controller;
import chess.controller.event.GameListener;
import chess.model.Game;
public abstract class Command {
public enum CommandResult {
/**
* The command was successfull. Should update display and switch player turn.
*/
Moved,
/** The command was successfull. Should not update anything */
NotMoved,
/** The command was successfull. Should only update display */
ActionNeeded,
/** The command was not successfull */
NotAllowed;
}
public abstract CommandResult execute(Game game, GameListener outputSystem);
}

View File

@@ -1,112 +0,0 @@
package chess.controller;
import chess.controller.Command.CommandResult;
import chess.controller.commands.UndoCommand;
import chess.controller.event.GameDispatcher;
import chess.controller.event.GameListener;
import chess.model.Game;
import chess.model.Game.GameStatus;
public class CommandExecutor {
private Game game;
private final GameDispatcher dispatcher;
public CommandExecutor() {
this(null);
}
public CommandExecutor(Game game) {
this(game, new GameDispatcher());
}
public CommandExecutor(Game game, GameDispatcher dispatcher) {
this.game = game;
this.dispatcher = dispatcher;
}
public synchronized CommandResult executeCommand(Command command) {
assert this.game != null : "No input game specified !";
CommandResult result = command.execute(this.game, this.dispatcher);
// non player commands are not supposed to return move result
assert result != CommandResult.Moved || command instanceof PlayerCommand || command instanceof UndoCommand;
processResult(command, result);
if (command instanceof PlayerCommand playerCommand && result != CommandResult.NotAllowed)
this.game.addAction(playerCommand);
return result;
}
private void processResult(Command command, CommandResult result) {
switch (result) {
case NotAllowed:
case NotMoved:
return;
case ActionNeeded:
this.dispatcher.onBoardUpdate();
return;
case Moved:
this.dispatcher.onBoardUpdate();
if (checkGameStatus()) {
this.dispatcher.onGameEnd();
return;
}
switchPlayerTurn(command instanceof UndoCommand);
return;
}
}
private void switchPlayerTurn(boolean undone) {
this.game.switchPlayerTurn();
this.dispatcher.onPlayerTurn(this.game.getPlayerTurn(), undone);
}
/**
*
* @return True if the game is over
*/
private boolean checkGameStatus() {
GameStatus gameStatus = this.game.checkGameStatus();
switch (gameStatus) {
case Draw:
this.dispatcher.onDraw();
return true;
case Check:
this.dispatcher.onKingInCheck();
return false;
case CheckMate:
this.dispatcher.onKingInMat();
this.dispatcher.onWin(this.game.getPlayerTurn());
return true;
case OnGoing:
return false;
case Pat:
this.dispatcher.onPatSituation();
return true;
}
return false;
}
public void addListener(GameListener listener) {
this.dispatcher.addListener(listener);
}
public void setGame(Game game) {
this.game = game;
}
public void close() {
this.dispatcher.stopService();
}
}

View File

@@ -0,0 +1,29 @@
package chess.controller;
import chess.model.Coordinates;
import chess.model.Game;
import chess.model.Move;
import chess.view.render2D.Window;
public class Controller {
private final Game game;
private final Window view;
public Controller(Game game, Window view) {
this.game = game;
this.view = view;
}
/**
*
* @param from Coordinates: old coordinates
* @param to Coordinates: new coordinates
*/
public void sendMove(Coordinates from, Coordinates to) {
System.out.println("New move: " + from + " to " + to);
game.setMove(new Move(from, to));
}
}

View File

@@ -1,15 +0,0 @@
package chess.controller;
import chess.controller.event.GameListener;
import chess.model.Game;
public abstract class PlayerCommand extends Command{
public CommandResult undo(Game game, GameListener outputSystem) {
CommandResult result = undoImpl(game, outputSystem);
game.updateLastMove();
return result;
}
protected abstract CommandResult undoImpl(Game game, GameListener outputSystem);
}

View File

@@ -1,67 +0,0 @@
package chess.controller.commands;
import chess.controller.PlayerCommand;
import chess.controller.event.GameListener;
import chess.model.ChessBoard;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Move;
public class CastlingCommand extends PlayerCommand {
private Move kingMove;
private Move rookMove;
private final boolean bigCastling;
public CastlingCommand(boolean bigCastling) {
this.bigCastling = bigCastling;
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
// we must promote the pending pawn before
if (board.pawnShouldBePromoted())
return CommandResult.NotAllowed;
if (bigCastling && !board.canBigCastle(game.getPlayerTurn()))
return CommandResult.NotAllowed;
if (!bigCastling && !board.canSmallCastle(game.getPlayerTurn()))
return CommandResult.NotAllowed;
int rookBeginX = bigCastling ? 0 : 7;
int rookEndX = bigCastling ? 3 : 5;
int kingBeginX = 4;
int kingEndX = bigCastling ? 2 : 6;
int colorLine = game.getPlayerTurn() == Color.White ? 7 : 0;
Coordinate kingCoords = new Coordinate(kingBeginX, colorLine);
Coordinate rookCoords = new Coordinate(rookBeginX, colorLine);
this.kingMove = new Move(kingCoords, new Coordinate(kingEndX, colorLine));
this.rookMove = new Move(rookCoords, new Coordinate(rookEndX, colorLine));
board.applyMove(this.kingMove);
board.applyMove(this.rookMove);
return CommandResult.Moved;
}
public boolean isBigCastling() {
return bigCastling;
}
@Override
protected CommandResult undoImpl(Game game, GameListener outputSystem) {
game.getBoard().undoMove(this.kingMove, null);
game.getBoard().undoMove(this.rookMove, null);
return CommandResult.Moved;
}
}

View File

@@ -1,37 +0,0 @@
package chess.controller.commands;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.Game;
public class GetAllowedCastlingsCommand extends Command{
public enum CastlingResult {
None, Small, Big, Both;
}
private CastlingResult castlingResult;
public GetAllowedCastlingsCommand() {}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
boolean canSmallCastle = game.getBoard().canSmallCastle(game.getPlayerTurn());
boolean canBigCastle = game.getBoard().canBigCastle(game.getPlayerTurn());
int result = 0;
if (canSmallCastle)
result += 1;
if (canBigCastle)
result += 2;
this.castlingResult = CastlingResult.values()[result];
return CommandResult.NotMoved;
}
public CastlingResult getCastlingResult() {
return castlingResult;
}
}

View File

@@ -1,41 +0,0 @@
package chess.controller.commands;
import java.util.ArrayList;
import java.util.List;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.ChessBoard;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Piece;
public class GetAllowedMovesPieceCommand extends Command {
private final Coordinate start;
private List<Coordinate> destinations;
public GetAllowedMovesPieceCommand(Coordinate start) {
this.start = start;
this.destinations = new ArrayList<>();
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
Piece piece = board.pieceAt(start);
if (piece == null)
return CommandResult.NotAllowed;
if (piece.getColor() != game.getPlayerTurn())
return CommandResult.NotAllowed;
this.destinations = board.getAllowedMoves(start);
return CommandResult.NotMoved;
}
public List<Coordinate> getDestinations() {
return destinations;
}
}

View File

@@ -1,33 +0,0 @@
package chess.controller.commands;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Piece;
public class GetPieceAtCommand extends Command{
private final Coordinate pieceCoords;
private Piece piece;
public GetPieceAtCommand(Coordinate pieceCoords) {
this.pieceCoords = pieceCoords;
this.piece = null;
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
if (!pieceCoords.isValid())
return CommandResult.NotAllowed;
this.piece = game.getBoard().pieceAt(pieceCoords);
return CommandResult.NotMoved;
}
public Piece getPiece() {
return piece;
}
}

View File

@@ -1,24 +0,0 @@
package chess.controller.commands;
import java.util.List;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.Game;
import chess.model.Move;
public class GetPlayerMovesCommand extends Command {
private List<Move> moves;
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
this.moves = game.getBoard().getAllowedMoves(game.getPlayerTurn());
return CommandResult.NotMoved;
}
public List<Move> getMoves() {
return moves;
}
}

View File

@@ -1,105 +0,0 @@
package chess.controller.commands;
import chess.controller.PlayerCommand;
import chess.controller.event.GameListener;
import chess.model.ChessBoard;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Move;
import chess.model.Piece;
import chess.model.visitor.PiecePathChecker;
public class MoveCommand extends PlayerCommand {
private final Move move;
private Piece deadPiece;
public MoveCommand(Move move) {
this.move = move;
this.deadPiece = null;
}
public Move getMove() {
return move;
}
public Piece getDeadPiece() {
return deadPiece;
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
CommandResult result = processMove(game, outputSystem);
switch (result) {
case NotAllowed:
outputSystem.onMoveNotAllowed(this.move);
return result;
case Moved:
outputSystem.onMove(this.move);
game.saveTraitPiecesPos();
return result;
case ActionNeeded:
case NotMoved:
return result;
}
return null;
}
private CommandResult processMove(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
// we must promote the pending pawn before
if (board.pawnShouldBePromoted())
return CommandResult.NotAllowed;
Piece piece = board.pieceAt(move.getStart());
if (piece == null)
return CommandResult.NotAllowed;
if (piece.getColor() != game.getPlayerTurn())
return CommandResult.NotAllowed;
boolean valid = new PiecePathChecker(board, move).isValid();
if (!valid)
return CommandResult.NotAllowed;
this.deadPiece = board.pieceAt(move.getDeadPieceCoords());
board.applyMove(move);
if (board.isKingInCheck(game.getPlayerTurn())) {
board.undoLastMove();
return CommandResult.NotAllowed;
}
if (tryPromote(game, outputSystem)) {
return CommandResult.ActionNeeded;
}
board.setLastMove(this.move);
return CommandResult.Moved;
}
@Override
protected CommandResult undoImpl(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
game.undoTraitPiecesPos();
board.undoMove(move, deadPiece);
return CommandResult.Moved;
}
private boolean tryPromote(Game game, GameListener outputSystem) {
Coordinate pawnPos = game.getBoard().pawnPromotePosition();
if (pawnPos == null)
return false;
outputSystem.onPromotePawn(pawnPos);
return true;
}
}

View File

@@ -1,58 +0,0 @@
package chess.controller.commands;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.ChessBoard;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class NewGameCommand extends Command {
public CommandResult execute(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
board.clearBoard();
for (int i = 0; i < 8; i++) {
board.pieceComes(new Pawn(Color.Black), new Coordinate(i, 1));
board.pieceComes(new Pawn(Color.White), new Coordinate(i, Coordinate.VALUE_MAX - 2));
}
board.pieceComes(new Rook(Color.Black), new Coordinate(0, 0));
board.pieceComes(new Rook(Color.Black), new Coordinate(Coordinate.VALUE_MAX - 1, 0));
board.pieceComes(new Rook(Color.White), new Coordinate(0, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Rook(Color.White), new Coordinate(Coordinate.VALUE_MAX - 1, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Knight(Color.Black), new Coordinate(1, 0));
board.pieceComes(new Knight(Color.Black), new Coordinate(Coordinate.VALUE_MAX - 2, 0));
board.pieceComes(new Knight(Color.White), new Coordinate(1, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Knight(Color.White), new Coordinate(Coordinate.VALUE_MAX - 2, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Bishop(Color.Black), new Coordinate(2, 0));
board.pieceComes(new Bishop(Color.Black), new Coordinate(Coordinate.VALUE_MAX - 3, 0));
board.pieceComes(new Bishop(Color.White), new Coordinate(2, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Bishop(Color.White), new Coordinate(Coordinate.VALUE_MAX - 3, Coordinate.VALUE_MAX - 1));
board.pieceComes(new Queen(Color.Black), new Coordinate(3, 0));
board.pieceComes(new King(Color.Black), new Coordinate(4, 0));
board.pieceComes(new Queen(Color.White), new Coordinate(3, Coordinate.VALUE_MAX - 1));
board.pieceComes(new King(Color.White), new Coordinate(4, Coordinate.VALUE_MAX - 1));
game.reset();
outputSystem.onGameStart();
outputSystem.onPlayerTurn(game.getPlayerTurn(), false);
return CommandResult.NotMoved;
}
}

View File

@@ -1,101 +0,0 @@
package chess.controller.commands;
import chess.controller.PlayerCommand;
import chess.controller.event.GameListener;
import chess.model.ChessBoard;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Piece;
import chess.model.pieces.Bishop;
import chess.model.pieces.Knight;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
import chess.model.visitor.PawnIdentifier;
public class PromoteCommand extends PlayerCommand {
public enum PromoteType {
Queen,
Rook,
Bishop,
Knight
}
private final PromoteType promoteType;
private Coordinate pieceCoords;
private Piece oldPawn;
public PromoteCommand(PromoteType promoteType) {
this.promoteType = promoteType;
this.pieceCoords = null;
this.oldPawn = null;
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
this.pieceCoords = board.pawnPromotePosition();
if (this.pieceCoords == null)
return CommandResult.NotAllowed;
Piece pawn = board.pieceAt(this.pieceCoords);
if (!new PawnIdentifier(game.getPlayerTurn()).isPawn(pawn))
return CommandResult.NotAllowed;
int destY = this.pieceCoords.getY();
int enemyLine = pawn.getColor() == Color.White ? 0 : 7;
if (destY != enemyLine)
return CommandResult.NotAllowed;
this.oldPawn = pawn;
board.pieceComes(createPiece(this.promoteType, pawn.getColor()), this.pieceCoords);
return CommandResult.Moved;
}
private Piece createPiece(PromoteType promoteType, Color color) {
switch (promoteType) {
case Queen:
return new Queen(color);
case Bishop:
return new Bishop(color);
case Knight:
return new Knight(color);
case Rook:
return new Rook(color);
default:
return null;
}
}
@Override
protected CommandResult undoImpl(Game game, GameListener outputSystem) {
final ChessBoard board = game.getBoard();
Piece promoted = board.pieceAt(this.pieceCoords);
assert promoted != null;
game.undoTraitPiecesPos();
board.pieceComes(this.oldPawn, this.pieceCoords);
game.getLastAction().undo(game, outputSystem);
return CommandResult.Moved;
}
public PromoteType getPromoteType() {
return promoteType;
}
}

View File

@@ -1,24 +0,0 @@
package chess.controller.commands;
import chess.controller.Command;
import chess.controller.event.GameListener;
import chess.model.Color;
import chess.model.Game;
public class SurrenderCommand extends Command {
private final Color player;
public SurrenderCommand(Color player) {
this.player = player;
}
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
outputSystem.onSurrender(player);
outputSystem.onWin(Color.getEnemy(player));
outputSystem.onGameEnd();
return CommandResult.NotMoved;
}
}

View File

@@ -1,19 +0,0 @@
package chess.controller.commands;
import chess.controller.Command;
import chess.controller.PlayerCommand;
import chess.controller.event.GameListener;
import chess.model.Game;
public class UndoCommand extends Command{
@Override
public CommandResult execute(Game game, GameListener outputSystem) {
PlayerCommand lastAction = game.getLastAction();
if (lastAction == null)
return CommandResult.NotAllowed;
return lastAction.undo(game, outputSystem);
}
}

View File

@@ -1,61 +0,0 @@
package chess.controller.event;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
public class EmptyGameDispatcher extends GameDispatcher {
@Override
public void onBoardUpdate() {
}
@Override
public void onDraw() {
}
@Override
public void onGameEnd() {
}
@Override
public void onGameStart() {
}
@Override
public void onKingInCheck() {
}
@Override
public void onKingInMat() {
}
@Override
public void onMove(Move move) {
}
@Override
public void onMoveNotAllowed(Move move) {
}
@Override
public void onPatSituation() {
}
@Override
public void onPlayerTurn(Color color, boolean undone) {
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
}
@Override
public void onSurrender(Color coward) {
}
@Override
public void onWin(Color winner) {
}
}

View File

@@ -1,48 +0,0 @@
package chess.controller.event;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
public abstract class GameAdaptator implements GameListener {
@Override
public void onPlayerTurn(Color color, boolean undone) {}
@Override
public void onWin(Color color) {}
@Override
public void onKingInCheck() {}
@Override
public void onKingInMat() {}
@Override
public void onPatSituation() {}
@Override
public void onSurrender(Color color) {}
@Override
public void onGameStart() {}
@Override
public void onPromotePawn(Coordinate pieceCoords) {}
@Override
public void onBoardUpdate() {}
@Override
public void onGameEnd() {}
@Override
public void onMove(Move move) {}
@Override
public void onMoveNotAllowed(Move move) {}
@Override
public void onDraw() {}
}

View File

@@ -1,100 +0,0 @@
package chess.controller.event;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
public class GameDispatcher implements GameListener {
private final List<GameListener> listeners;
private final ExecutorService executor;
public GameDispatcher() {
this.listeners = new ArrayList<>();
this.executor = Executors.newSingleThreadExecutor();
}
public void addListener(GameListener listener) {
this.listeners.add(listener);
}
private void asyncForEachCall(Consumer<GameListener> func) {
this.executor.execute(() -> this.listeners.forEach(func));
}
@Override
public void onPlayerTurn(Color color, boolean undone) {
asyncForEachCall((l) -> l.onPlayerTurn(color, undone));
}
@Override
public void onWin(Color color) {
asyncForEachCall((l) -> l.onWin(color));
}
@Override
public void onKingInCheck() {
asyncForEachCall((l) -> l.onKingInCheck());
}
@Override
public void onKingInMat() {
asyncForEachCall((l) -> l.onKingInMat());
}
@Override
public void onPatSituation() {
asyncForEachCall((l) -> l.onPatSituation());
}
@Override
public void onSurrender(Color color) {
asyncForEachCall((l) -> l.onSurrender(color));
}
@Override
public void onGameStart() {
asyncForEachCall((l) -> l.onGameStart());
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
asyncForEachCall((l) -> l.onPromotePawn(pieceCoords));
}
@Override
public void onBoardUpdate() {
asyncForEachCall((l) -> l.onBoardUpdate());
}
@Override
public void onGameEnd() {
asyncForEachCall((l) -> l.onGameEnd());
}
@Override
public void onMove(Move move) {
asyncForEachCall((l) -> l.onMove(move));
}
@Override
public void onMoveNotAllowed(Move move) {
asyncForEachCall((l) -> l.onMoveNotAllowed(move));
}
@Override
public void onDraw() {
asyncForEachCall((l) -> l.onDraw());
}
public void stopService() {
this.executor.shutdown();
}
}

View File

@@ -1,81 +0,0 @@
package chess.controller.event;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
public interface GameListener {
/**
* Invoked when the display of the board should be updated
*/
void onBoardUpdate();
/**
* Invoked when a draw occurs (same position is repeated three times)
*/
void onDraw();
/**
* Invoked when the game has ended (by a win or a draw)
*/
void onGameEnd();
/**
* Invoked when the game has started
*/
void onGameStart();
/**
* Invoked when a king is in check
*/
void onKingInCheck();
/**
* Invoked when a checkmate occurs
*/
void onKingInMat();
/**
* Invoked when a valid move on the board occurs
* @param move the move to be processed
*/
void onMove(Move move);
/**
* Invoked when a sent move is not allowed
* @param move the move to be processed
*/
void onMoveNotAllowed(Move move);
/**
* Invoked when a pat situation occurs
*/
void onPatSituation();
/**
* Invoked when it's the player turn
* @param color the color of the player who should play
* @param undone true if it's a result of an undo command
*/
void onPlayerTurn(Color color, boolean undone);
/**
* Invoked when a pawn should be promoted
* @param pieceCoords the coordinates of the pawn
*/
void onPromotePawn(Coordinate pieceCoords);
/**
* Invoked when a players surrenders
* @param coward the player who gave up
*/
void onSurrender(Color coward);
/**
* Invoked when a player wins (by checkmate or if the other one surrenders)
* @param winner
*/
void onWin(Color winner);
}

View File

@@ -0,0 +1,17 @@
package chess.model;
import java.util.ArrayList;
public abstract class AccessibleCellsDecorator {
public AccessibleCellsDecorator base;
public ArrayList<Cell> cells;
protected abstract ArrayList<Cell> getAccessibleCells();
public AccessibleCellsDecorator(AccessibleCellsDecorator base) {
this.base = base;
}
public AccessibleCellsDecorator() {
this(null);
}
}

View File

@@ -0,0 +1,81 @@
package chess.model;
import chess.model.pieces.*;
public class Board {
private final Cell[][] cells;
private static final int WHITE = 0;
private static final int BLACK = 1;
public Board(int size) {
cells = new Cell[size][size];
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
cells[y][x] = new Cell();
}
}
setPieces();
}
public Board() {
this(8);
}
public Cell getCell(Coordinates coordinates) {
return cells[coordinates.getX()][coordinates.getY()];
}
public int getSize() {
return cells.length;
}
public void setPieces() throws Error {
if (this.getSize() == 8) {
setPiecesSize8();
} else {
throw new Error("Oops. This isn't implemented yet.");
}
}
public void setPiecesSize8() {
for (int i = 0; i < 8; i++) {
cells[i][1].setPiece(new Pawn(BLACK, this, cells[i][1]));
cells[i][6].setPiece(new Pawn(WHITE, this, cells[i][6]));
}
for (int i = 0; i <= 7; i += 7) {
cells[i][0].setPiece(new Rook(BLACK, this, cells[i][0]));
cells[i][7].setPiece(new Rook(WHITE, this, cells[i][7]));
}
for (int i = 1; i <= 6; i += 5) {
cells[i][0].setPiece(new Knight(BLACK, this, cells[i][0]));
cells[i][7].setPiece(new Knight(WHITE, this, cells[i][7]));
}
for (int i = 2; i <= 5; i += 3) {
cells[i][0].setPiece(new Bishop(BLACK, this, cells[i][0]));
cells[i][7].setPiece(new Bishop(WHITE, this, cells[i][7]));
}
cells[3][0].setPiece(new Queen(BLACK, this, cells[3][0]));
cells[3][7].setPiece(new Queen(WHITE, this, cells[3][7]));
cells[4][0].setPiece(new King(BLACK, this, cells[4][0]));
cells[4][7].setPiece(new King(WHITE, this, cells[4][7]));
}
public Piece getPiece(Coordinates coordinates) {
return getCell(coordinates).getPiece();
}
public void movePiece(Move move) throws Error {
Piece movingPiece = getPiece(move.getStart());
Cell arrivalCell = getCell(move.getEnd());
getCell(move.getStart()).deletePiece();
arrivalCell.setPiece(movingPiece);
movingPiece.setPosition(arrivalCell);
}
public Cell getRelativePosition(Coordinates coordinates) {
// todo
return null;
}
}

View File

@@ -0,0 +1,25 @@
package chess.model;
public class Cell {
private Piece piece = null;
public Cell(Piece piece) {
this.piece = piece;
}
public Cell(){
this.piece = null;
}
public void setPiece(Piece piece) {
this.piece = piece;
}
public void deletePiece() {
this.piece = null;
}
public Piece getPiece() {
return piece;
}
}

View File

@@ -1,312 +0,0 @@
package chess.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import chess.model.visitor.KingIdentifier;
import chess.model.visitor.PawnIdentifier;
import chess.model.visitor.PiecePathChecker;
public class ChessBoard {
public static class Cell {
private Piece piece;
public Cell() {
this.piece = null;
}
public Piece getPiece() {
return piece;
}
public void setPiece(Piece piece) {
this.piece = piece;
}
}
private final Cell[][] cells;
private Move lastVirtualMove;
private Move lastMove;
private Piece lastEjectedPiece;
public ChessBoard() {
this.cells = new Cell[Coordinate.VALUE_MAX][Coordinate.VALUE_MAX];
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
this.cells[i][j] = new Cell();
}
}
this.lastVirtualMove = null;
this.lastMove = null;
this.lastEjectedPiece = null;
}
public void applyMove(Move move) {
assert move.isValid() : "Invalid move !";
Piece deadPiece = pieceAt(move.getDeadPieceCoords());
if (deadPiece != null) {
this.lastEjectedPiece = deadPiece;
} else {
this.lastEjectedPiece = null;
}
Piece movingPiece = pieceAt(move.getStart());
pieceLeaves(move.getDeadPieceCoords());
pieceLeaves(move.getStart());
pieceComes(movingPiece, move.getFinish());
movingPiece.move();
this.lastVirtualMove = move;
}
public void undoLastMove() {
assert this.lastVirtualMove != null : "Can't undo at the beginning!";
undoMove(this.lastVirtualMove, this.lastEjectedPiece);
}
public void undoMove(Move move, Piece deadPiece) {
Piece movingPiece = pieceAt(move.getFinish());
pieceComes(movingPiece, move.getStart());
pieceLeaves(move.getFinish());
pieceComes(deadPiece, move.getDeadPieceCoords());
assert movingPiece != null;
movingPiece.unMove();
}
public boolean isCellEmpty(Coordinate coordinate) {
return pieceAt(coordinate) == null;
}
public Piece pieceAt(Coordinate coordinate) {
if (!coordinate.isValid())
return null;
return cellAt(coordinate).getPiece();
}
public void clearBoard() {
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
pieceLeaves(new Coordinate(i, j));
}
}
}
private Cell cellAt(Coordinate coordinate) {
return this.cells[coordinate.getX()][coordinate.getY()];
}
public void pieceComes(Piece piece, Coordinate coordinate) {
cellAt(coordinate).setPiece(piece);
}
public void pieceLeaves(Coordinate coordinate) {
cellAt(coordinate).setPiece(null);
}
public Coordinate findKing(Color color) {
KingIdentifier kingIdentifier = new KingIdentifier(color);
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate coordinate = new Coordinate(i, j);
Piece piece = pieceAt(coordinate);
if (kingIdentifier.isKing(piece)) {
return coordinate;
}
}
}
assert false : "No king found ?!";
return null;
}
public boolean isKingInCheck(Color color) {
Coordinate kingPos = findKing(color);
assert kingPos.isValid() : "King position is invalid!";
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate attackCoords = new Coordinate(i, j);
Piece attackPiece = pieceAt(attackCoords);
if (attackPiece == null)
continue;
PiecePathChecker checker = new PiecePathChecker(this, new Move(attackCoords, kingPos));
if (checker.isValid())
return true;
}
}
return false;
}
public boolean hasAllowedMoves(Color player) {
return !getAllowedMoves(player).isEmpty();
}
public List<Move> getAllowedMoves(Color player) {
List<Move> result = new ArrayList<>();
for (int x = 0; x < Coordinate.VALUE_MAX; x++) {
for (int y = 0; y < Coordinate.VALUE_MAX; y++) {
Coordinate start = new Coordinate(x, y);
Piece piece = pieceAt(start);
if (piece == null || piece.getColor() != player)
continue;
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate destination = new Coordinate(i, j);
Move move = new Move(start, destination);
PiecePathChecker piecePathChecker = new PiecePathChecker(this,
move);
if (!piecePathChecker.isValid())
continue;
applyMove(move);
if (!isKingInCheck(player))
result.add(move);
undoLastMove();
}
}
}
}
return result;
}
public List<Coordinate> getAllowedMoves(Coordinate pieceCoords) {
Piece piece = pieceAt(pieceCoords);
if (piece == null)
return null;
Color player = piece.getColor();
List<Coordinate> result = new ArrayList<>();
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate destination = new Coordinate(i, j);
Move move = new Move(pieceCoords, destination);
PiecePathChecker piecePathChecker = new PiecePathChecker(this,
move);
if (!piecePathChecker.isValid())
continue;
applyMove(move);
if (!isKingInCheck(player))
result.add(destination);
undoLastMove();
}
}
return result;
}
private boolean canCastle(Color color, int rookX, Direction kingDirection) {
if (isKingInCheck(color))
return false;
int colorLine = color == Color.White ? 7 : 0;
Coordinate kingCoords = new Coordinate(4, colorLine);
Coordinate rookCoords = new Coordinate(rookX, colorLine);
Piece king = pieceAt(kingCoords);
Piece rook = pieceAt(rookCoords);
if (king == null || rook == null || king.hasMoved() || rook.hasMoved())
return false;
for (int step = 1; step <= 2; step++) {
Coordinate dest = Coordinate.fromIndex(kingCoords.toIndex() + step * kingDirection.getIndexOffset());
Piece obstacle = pieceAt(dest);
if (obstacle != null)
return false;
applyMove(new Move(kingCoords, dest));
if (isKingInCheck(color)) {
undoLastMove();
return false;
}
undoLastMove();
}
Coordinate rookObstacleCoords = Coordinate.fromIndex(rookCoords.toIndex() - kingDirection.getIndexOffset());
Piece obstacle = pieceAt(rookObstacleCoords);
return obstacle == null;
}
public boolean canSmallCastle(Color color) {
return canCastle(color, 7, Direction.Right);
}
public boolean canBigCastle(Color color) {
return canCastle(color, 0, Direction.Left);
}
public boolean pawnShouldBePromoted() {
return pawnPromotePosition() != null;
}
/**
*
* @return Null if there is no pawn to promote
*/
public Coordinate pawnPromotePosition() {
Coordinate piecePos = pawnPromotePosition(Color.White);
if (piecePos != null)
return piecePos;
return pawnPromotePosition(Color.Black);
}
/**
*
* @return Null if there is no pawn to promote
*/
private Coordinate pawnPromotePosition(Color color) {
int enemyLineY = color == Color.White ? 0 : 7;
PawnIdentifier identifier = new PawnIdentifier(color);
for (int x = 0; x < Coordinate.VALUE_MAX; x++) {
Coordinate pieceCoords = new Coordinate(x, enemyLineY);
Piece piece = pieceAt(pieceCoords);
if (identifier.isPawn(piece))
return pieceCoords;
}
return null;
}
@Override
public int hashCode() {
int result = 0;
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Piece piece = pieceAt(new Coordinate(i, j));
if (piece == null)
continue;
result = Objects.hash(result, piece.getColor(), new Coordinate(i, j), piece);
}
}
return result;
}
public Move getLastMove() {
return this.lastMove;
}
public void setLastMove(Move lastMove) {
this.lastMove = lastMove;
}
@Override
public boolean equals(Object obj){
if (obj instanceof ChessBoard board)
return board.hashCode() == this.hashCode();
return false;
}
}

View File

@@ -1,10 +0,0 @@
package chess.model;
public enum Color {
White,
Black;
public static Color getEnemy(Color color) {
return color == White ? Black : White;
}
}

View File

@@ -1,52 +0,0 @@
package chess.model;
import java.util.Objects;
public class Coordinate {
private final int x;
private final int y;
public static int VALUE_MAX = 8;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public boolean isValid() {
return 0 <= this.x && this.x < VALUE_MAX && 0 <= this.y && this.y < VALUE_MAX;
}
public static Coordinate fromIndex(int index) {
return new Coordinate(index % VALUE_MAX, index / VALUE_MAX);
}
public int toIndex() {
return this.y * VALUE_MAX + this.x;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Coordinate coo) {
return this.x == coo.x && this.y == coo.y;
}
return false;
}
public String toString() {
return "(" + this.x + ", " + this.y + ")";
}
@Override
public int hashCode() {
return Objects.hash(this.x, this.y);
}
}

View File

@@ -0,0 +1,22 @@
package chess.model;
public class Coordinates {
private int x;
private int y;
public Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public String toString() {
return "(" + this.x + ", " + this.y + ")";
}
}

View File

@@ -1,54 +0,0 @@
package chess.model;
public enum Direction {
Unset(65),
Front(-8), Back(8), Left(-1), Right(1),
FrontLeft(-9), FrontRight(-7), BackLeft(7), BackRight(9);
private final int indexOffset;
Direction(int indexOffset) {
this.indexOffset = indexOffset;
}
public int getIndexOffset() {
return indexOffset;
}
public static Direction fromInt(int direction) {
for (Direction dir : Direction.values()) {
if (dir.getIndexOffset() == direction)
return dir;
}
return null;
}
public static Direction findDirection(Move move) {
assert move.isValid() : "Move is invalid!";
int diffX = move.getFinish().getX() - move.getStart().getX();
int diffY = move.getFinish().getY() - move.getStart().getY();
if (diffX == 0 && diffY < 0)
return Direction.Front;
if (diffX == 0 && diffY > 0)
return Direction.Back;
if (diffX < 0 && diffY == 0)
return Direction.Left;
if (diffX > 0 && diffY == 0)
return Direction.Right;
if (diffX < 0 && -diffX == diffY)
return Direction.BackLeft;
if (diffX > 0 && diffX == diffY)
return Direction.BackRight;
if (diffY < 0 && diffX == diffY)
return Direction.FrontLeft;
if (diffX > 0 && diffX == -diffY)
return Direction.FrontRight;
return Direction.Unset;
}
}

View File

@@ -1,122 +1,87 @@
package chess.model; package chess.model;
import java.util.HashMap; import java.util.ArrayList;
import java.util.List; import java.util.Observable;
import java.util.Map;
import java.util.Stack;
import chess.controller.PlayerCommand; public class Game extends Observable implements Runnable {
import chess.controller.commands.MoveCommand; private final Board board;
private Move move;
private final ArrayList<Player> players = new ArrayList<>();
public class Game { public Game(int size) {
private final ChessBoard board; this.board = new Board(size);
private Color playerTurn; this.setChanged();
private final Stack<PlayerCommand> movesHistory; this.notifyObservers();
private final Map<Integer, Integer> traitsPos; }
public Game() {
this(8);
}
private static final int DRAW_REPETITONS = 3; public int getSize(){
return board.getSize();
}
public Board getBoard() {
return board;
}
public Move getMove() {
return move;
}
public enum GameStatus { public void setMove(Move m){
Draw, Check, CheckMate, OnGoing, Pat; this.move = m;
} synchronized (this) {
this.notify();
}
}
public Game() { public boolean gameDone(){
this.board = new ChessBoard(); // todo
this.movesHistory = new Stack<>(); return false;
this.traitsPos = new HashMap<>(); }
}
public ChessBoard getBoard() { public Player getNextPlayer(){
return board; return players.getFirst();
} }
public Color getPlayerTurn() { public void updatePlayersList(){
return playerTurn; Player player = players.getFirst();
} players.remove(player);
players.add(player);
}
public void reset() { public void playGame() {
resetPlayerTurn(); players.add(new Player(this));
this.traitsPos.clear(); players.add(new Player(this));
} while (!gameDone()){
Move m = getNextPlayer().getMove();
while (!applyMove(m)){
m = getNextPlayer().getMove();
}
updatePlayersList();
setChanged();
notifyObservers();
}
}
public void resetPlayerTurn() { @Override
this.playerTurn = Color.White; public void run(){
} playGame();
}
/** public boolean applyMove(Move m){
* if (board.getCell(m.getStart()).getPiece() == null){
* @param color the current player turn return false;
* @return true if a draw should be declared }
*/ try {
public void saveTraitPiecesPos() { move(m);
int piecesHash = this.board.hashCode(); }
Integer count = this.traitsPos.get(piecesHash); catch (Exception e) {
this.traitsPos.put(piecesHash, count == null ? 1 : count + 1); return false;
} }
return true;
}
/**
* @return whether the game is in a draw situation
*/
private boolean checkDraw() {
return this.traitsPos.containsValue(DRAW_REPETITONS);
}
/**
*
* @return true if a draw should occur
*/
public void switchPlayerTurn() {
playerTurn = Color.getEnemy(playerTurn);
}
public GameStatus checkGameStatus() {
final Color enemy = Color.getEnemy(getPlayerTurn());
if (checkDraw())
return GameStatus.Draw;
if (this.board.isKingInCheck(enemy))
if (this.board.hasAllowedMoves(enemy))
return GameStatus.Check;
else
return GameStatus.CheckMate;
if (!board.hasAllowedMoves(enemy))
return GameStatus.Pat;
return GameStatus.OnGoing;
}
public void addAction(PlayerCommand command) {
this.movesHistory.add(command);
}
public PlayerCommand getLastAction() {
if (this.movesHistory.isEmpty())
return null;
return this.movesHistory.pop();
}
public void updateLastMove() {
if (this.movesHistory.isEmpty())
return;
PlayerCommand last = this.movesHistory.getLast();
if (last instanceof MoveCommand move) {
this.board.setLastMove(move.getMove());
}
}
public void undoTraitPiecesPos() {
int piecesHash = this.board.hashCode();
Integer count = this.traitsPos.get(piecesHash);
if (count != null)
this.traitsPos.put(piecesHash, count - 1);
}
public List<PlayerCommand> getMoves() {
return this.movesHistory;
}
public void move(Move move) throws Exception{
board.movePiece(move);
}
} }

View File

@@ -0,0 +1,15 @@
package chess.model;
import java.util.Observable;
public class Model2D extends Observable {
public int x;
public int y;
public void set(int i, int j){
this.x = i;
this.y = j;
setChanged();
notifyObservers();
}
}

View File

@@ -1,63 +1,26 @@
package chess.model; package chess.model;
public class Move { public class Move {
private final Coordinate start; private Coordinates start;
private final Coordinate finish; private Coordinates end;
private Coordinate deadPieceCoords;
public Move(Coordinate start, Coordinate finish) { public Move(Coordinates start, Coordinates end) {
this.start = start; this.start = start;
this.finish = finish; this.end = end;
this.deadPieceCoords = finish; }
}
public boolean isValid() { public Coordinates getEnd() {
return this.start.isValid() && this.finish.isValid() && !this.start.equals(this.finish); return end;
} }
public Coordinate getStart() { public Coordinates getStart() {
return start; return start;
} }
public Coordinate getFinish() { public void updateMove(Coordinates start, Coordinates end) {
return finish; this.start = start;
} this.end = end;
}
public int traversedCells() {
assert isValid() : "Move is invalid!";
int diffX = getFinish().getX() - getStart().getX();
int diffY = getFinish().getY() - getStart().getY();
assert Math.abs(diffX) < Coordinate.VALUE_MAX : "Move is too big!";
assert Math.abs(diffY) < Coordinate.VALUE_MAX : "Move is too big!";
if (diffX == 0)
return Math.abs(diffY);
if (diffY == 0)
return Math.abs(diffX);
if (Math.abs(diffX) == Math.abs(diffY))
return Math.abs(diffX);
return 0;
}
public Coordinate getMiddle() {
return Coordinate.fromIndex((getStart().toIndex() + getFinish().toIndex()) / 2);
}
public void setDeadPieceCoords(Coordinate deadCoords) {
this.deadPieceCoords = deadCoords;
}
public Coordinate getDeadPieceCoords() {
return deadPieceCoords;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Move other)
return this.start.equals(other.start) && this.finish.equals(other.finish);
return false;
}
} }

View File

@@ -1,34 +1,27 @@
package chess.model; package chess.model;
import chess.view.render2D.Pieces;
public abstract class Piece { public abstract class Piece {
public final int color;
public AccessibleCellsDecorator decorator;
public final Board board;
public Cell position;
private final Color color; public Piece(int color, Board board, Cell position) {
private int moved; this.color = color;
this.board = board;
this.position = position;
}
public Piece(Color color) { public int getColor() {
this.color = color; return color;
this.moved = 0; }
}
public void move() { public void setPosition(Cell cell) {
this.moved++; this.position = cell;
} }
public Color getColor() { public abstract void accept(PieceVisitor pv);
return color;
}
public boolean hasMoved() {
return moved > 0;
}
public void unMove() {
this.moved--;
}
public abstract <T> T accept(PieceVisitor<T> visitor);
@Override
public abstract boolean equals(Object other);
} }

View File

@@ -1,28 +1,17 @@
package chess.model; package chess.model;
import chess.model.pieces.Bishop; import chess.model.pieces.*;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public interface PieceVisitor<T> { public interface PieceVisitor{
default void visit(Piece p) {
p.accept(this);
}
default T visit(Piece piece) { void visitPiece(Bishop p);
return piece.accept(this); void visitPiece(Knight p);
} void visitPiece(Pawn p);
void visitPiece(Queen p);
T visitPiece(Bishop bishop); void visitPiece(King p);
void visitPiece(Rook p);
T visitPiece(King king);
T visitPiece(Knight knight);
T visitPiece(Pawn pawn);
T visitPiece(Queen queen);
T visitPiece(Rook rook);
} }

View File

@@ -0,0 +1,21 @@
package chess.model;
public class Player {
private final Game game;
public Player(Game game) {
this.game = game;
}
public Move getMove(){
synchronized(game){
try {
game.wait();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
return game.getMove();
}
}

View File

@@ -0,0 +1,21 @@
package chess.model.decorators;
import chess.model.AccessibleCellsDecorator;
import chess.model.Cell;
import java.util.ArrayList;
public class DiagonalDecorator extends AccessibleCellsDecorator {
public DiagonalDecorator(AccessibleCellsDecorator base) {
super(base);
}
public DiagonalDecorator(){
super();
}
@Override
protected ArrayList<Cell> getAccessibleCells() {
return null;
}
}

View File

@@ -0,0 +1,22 @@
package chess.model.decorators;
import chess.model.AccessibleCellsDecorator;
import chess.model.Cell;
import java.util.ArrayList;
public class LineDecorator extends AccessibleCellsDecorator {
public LineDecorator(AccessibleCellsDecorator base) {
super(base);
}
public LineDecorator(){
super();
}
@Override
protected ArrayList<Cell> getAccessibleCells() {
return null;
}
}

View File

@@ -1,27 +1,17 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.Board;
import chess.model.Cell;
import chess.model.Piece; import chess.model.Piece;
import chess.model.PieceVisitor; import chess.model.PieceVisitor;
public class Bishop extends Piece { public class Bishop extends Piece {
public Bishop(final int color, Board board, Cell cell) {
super(color, board, cell);
}
public Bishop(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
@Override
public int hashCode() {
return 0;
}
public boolean equals(Object obj) {
return (obj instanceof Bishop && ((Bishop) obj).getColor() == this.getColor());
}
} }

View File

@@ -1,26 +1,17 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.Board;
import chess.model.Cell;
import chess.model.Piece; import chess.model.Piece;
import chess.model.PieceVisitor; import chess.model.PieceVisitor;
public class King extends Piece { public class King extends Piece {
public King(final int color, Board board, Cell cell) {
super(color, board, cell);
}
public King(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
@Override
public int hashCode() {
return 1;
}
public boolean equals(Object obj) {
return (obj instanceof King && ((King) obj).getColor() == this.getColor());
}
} }

View File

@@ -1,26 +1,17 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.Board;
import chess.model.Cell;
import chess.model.Piece; import chess.model.Piece;
import chess.model.PieceVisitor; import chess.model.PieceVisitor;
public class Knight extends Piece { public class Knight extends Piece {
public Knight(final int color, Board board, Cell cell) {
super(color, board, cell);
}
public Knight(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
@Override
public int hashCode() {
return 2;
}
public boolean equals(Object obj) {
return (obj instanceof Knight && ((Knight) obj).getColor() == this.getColor());
}
} }

View File

@@ -1,30 +1,17 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.Board;
import chess.model.Cell;
import chess.model.Piece; import chess.model.Piece;
import chess.model.PieceVisitor; import chess.model.PieceVisitor;
public class Pawn extends Piece { public class Pawn extends Piece {
public Pawn(final int color, Board board, Cell cell) {
super(color, board, cell);
}
public Pawn(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
public int multiplier() {
return getColor() == Color.White ? 1 : -1;
}
@Override
public int hashCode() {
return 3;
}
public boolean equals(Object obj) {
return (obj instanceof Pawn && ((Pawn) obj).getColor() == this.getColor());
}
} }

View File

@@ -1,26 +1,16 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.*;
import chess.model.Piece; import chess.model.decorators.DiagonalDecorator;
import chess.model.PieceVisitor; import chess.model.decorators.LineDecorator;
public class Queen extends Piece { public class Queen extends Piece {
public Queen(final int color, Board board, Cell cell) {
super(color, board, cell);
}
public Queen(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
@Override
public int hashCode() {
return 4;
}
public boolean equals(Object obj) {
return (obj instanceof Queen && ((Queen) obj).getColor() == this.getColor());
}
} }

View File

@@ -1,26 +1,21 @@
package chess.model.pieces; package chess.model.pieces;
import chess.model.Color; import chess.model.Board;
import chess.model.Cell;
import chess.model.Piece; import chess.model.Piece;
import chess.model.PieceVisitor; import chess.model.PieceVisitor;
import chess.model.decorators.DiagonalDecorator;
import chess.model.decorators.LineDecorator;
public class Rook extends Piece { public class Rook extends Piece {
public Rook(final int color, Board board, Cell cell) {
super(color, board, cell);
this.decorator = new LineDecorator();
// todo
}
public Rook(Color color) { @Override
super(color); public void accept(PieceVisitor pv) {
} pv.visitPiece(this);
}
@Override
public <T> T accept(PieceVisitor<T> visitor) {
return visitor.visitPiece(this);
}
@Override
public int hashCode() {
return 5;
}
public boolean equals(Object other) {
return (other instanceof Rook && ((Rook) other).getColor() == this.getColor());
}
} }

View File

@@ -1,52 +0,0 @@
package chess.model.visitor;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.*;
public class KingIdentifier implements PieceVisitor<Boolean> {
private final Color color;
public KingIdentifier(Color color) {
this.color = color;
}
public boolean isKing(Piece piece) {
if (piece == null)
return false;
return visit(piece);
}
@Override
public Boolean visitPiece(Bishop bishop) {
return false;
}
@Override
public Boolean visitPiece(King king) {
return king.getColor() == color;
}
@Override
public Boolean visitPiece(Knight knight) {
return false;
}
@Override
public Boolean visitPiece(Pawn pawn) {
return false;
}
@Override
public Boolean visitPiece(Queen queen) {
return false;
}
@Override
public Boolean visitPiece(Rook rook) {
return false;
}
}

View File

@@ -1,52 +0,0 @@
package chess.model.visitor;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.*;
public class PawnIdentifier implements PieceVisitor<Boolean> {
private final Color color;
public PawnIdentifier(Color color) {
this.color = color;
}
public boolean isPawn(Piece piece) {
if (piece == null)
return false;
return visit(piece);
}
@Override
public Boolean visitPiece(Bishop bishop) {
return false;
}
@Override
public Boolean visitPiece(King king) {
return false;
}
@Override
public Boolean visitPiece(Knight knight) {
return false;
}
@Override
public Boolean visitPiece(Pawn pawn) {
return pawn.getColor() == color;
}
@Override
public Boolean visitPiece(Queen queen) {
return false;
}
@Override
public Boolean visitPiece(Rook rook) {
return false;
}
}

View File

@@ -1,119 +0,0 @@
package chess.model.visitor;
import chess.model.Coordinate;
import chess.model.Direction;
import chess.model.Move;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class PermissiveRuleChecker implements PieceVisitor<Boolean> {
private final Move move;
public PermissiveRuleChecker(Move move) {
this.move = move;
assert move.isValid() : "Move is invalid!";
}
public boolean isValidFor(Piece piece) {
return visit(piece);
}
@Override
public Boolean visitPiece(Bishop bishop) {
Direction moveDirection = Direction.findDirection(this.move);
switch (moveDirection) {
case FrontLeft:
case BackLeft:
case FrontRight:
case BackRight:
return true;
default:
return false;
}
}
@Override
public Boolean visitPiece(King king) {
return this.move.traversedCells() == 1;
}
@Override
public Boolean visitPiece(Knight knight) {
Coordinate piecePos = move.getStart();
final Coordinate[] positions = {
new Coordinate(piecePos.getX() - 1, piecePos.getY() - 2),
new Coordinate(piecePos.getX() - 1, piecePos.getY() + 2),
new Coordinate(piecePos.getX() + 1, piecePos.getY() - 2),
new Coordinate(piecePos.getX() + 1, piecePos.getY() + 2),
new Coordinate(piecePos.getX() + 2, piecePos.getY() - 1),
new Coordinate(piecePos.getX() + 2, piecePos.getY() + 1),
new Coordinate(piecePos.getX() - 2, piecePos.getY() - 1),
new Coordinate(piecePos.getX() - 2, piecePos.getY() + 1),
};
for (int i = 0; i < positions.length; i++) {
if (this.move.getFinish().equals(positions[i]))
return true;
}
return false;
}
@Override
public Boolean visitPiece(Pawn pawn) {
Direction moveDirection = Direction.findDirection(this.move);
int directionIndexOffset = moveDirection.getIndexOffset();
int distance = this.move.traversedCells();
// Revoke moving backwards
if (directionIndexOffset * pawn.multiplier() > 0)
return false;
// Allowing straight moves
if (Math.abs(directionIndexOffset) == Math.abs(Direction.Front.getIndexOffset())) {
if (pawn.hasMoved())
return distance == 1;
return distance == 1 || distance == 2;
}
// Allowing small diagonal moves
if (directionIndexOffset * pawn.multiplier() == Direction.FrontLeft.getIndexOffset()
|| directionIndexOffset * pawn.multiplier() == Direction.FrontRight.getIndexOffset()) {
return distance == 1;
}
return false;
}
@Override
public Boolean visitPiece(Queen queen) {
Direction moveDirection = Direction.findDirection(this.move);
return moveDirection != Direction.Unset;
}
@Override
public Boolean visitPiece(Rook rook) {
Direction moveDirection = Direction.findDirection(this.move);
switch (moveDirection) {
case Front:
case Back:
case Left:
case Right:
return true;
default:
return false;
}
}
}

View File

@@ -1,151 +0,0 @@
package chess.model.visitor;
import chess.model.ChessBoard;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Direction;
import chess.model.Move;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class PiecePathChecker implements PieceVisitor<Boolean> {
private final ChessBoard board;
private final Move move;
public PiecePathChecker(ChessBoard board, Move move) {
this.move = move;
this.board = board;
}
public boolean isValid() {
if (this.move.getStart().equals(this.move.getFinish()))
return false;
Piece piece = this.board.pieceAt(move.getStart());
if (piece == null)
return false;
return visit(piece);
}
@Override
public Boolean visitPiece(Bishop bishop) {
return basicCheck(bishop);
}
@Override
public Boolean visitPiece(King king) {
return destCheck(king);
}
@Override
public Boolean visitPiece(Knight knight) {
return destCheck(knight);
}
@Override
public Boolean visitPiece(Queen queen) {
return basicCheck(queen);
}
@Override
public Boolean visitPiece(Rook rook) {
return basicCheck(rook);
}
@Override
public Boolean visitPiece(Pawn pawn) {
if (!new PermissiveRuleChecker(this.move).isValidFor(pawn))
return false;
Direction moveDirection = Direction.fromInt(Direction.findDirection(move).getIndexOffset() * pawn.multiplier());
if (moveDirection == Direction.Front)
return testPath(pawn.getColor()) && this.board.pieceAt(this.move.getFinish()) == null;
assert moveDirection == Direction.FrontLeft || moveDirection == Direction.FrontRight;
if (checkEnPassant())
return true;
Piece destPiece = this.board.pieceAt(this.move.getFinish());
if (destPiece == null)
return false;
return destPiece.getColor() != pawn.getColor();
}
private boolean checkEnPassant() {
Move lastMove = this.board.getLastMove();
if (lastMove == null)
return false;
Piece pieceToEat = this.board.pieceAt(lastMove.getFinish());
if (pieceToEat == null || !(pieceToEat instanceof Pawn))
return false;
Piece pawn = this.board.pieceAt(this.move.getStart());
if (pieceToEat.getColor() == pawn.getColor())
return false;
Direction lastMoveDir = Direction.findDirection(lastMove);
if ((lastMoveDir != Direction.Front && lastMoveDir != Direction.Back) || lastMove.traversedCells() != 2)
return false;
Coordinate middle = lastMove.getMiddle();
if (middle.equals(this.move.getFinish())
&& new PawnIdentifier(pieceToEat.getColor()).isPawn(pieceToEat)) {
this.move.setDeadPieceCoords(lastMove.getFinish());
return true;
}
return false;
}
private boolean destCheck(Piece piece) {
if (!new PermissiveRuleChecker(this.move).isValidFor(piece))
return false;
Piece destPiece = board.pieceAt(this.move.getFinish());
if (destPiece == null)
return true;
return destPiece.getColor() != piece.getColor();
}
private boolean basicCheck(Piece piece) {
if (!new PermissiveRuleChecker(this.move).isValidFor(piece))
return false;
return testPath(piece.getColor());
}
private boolean testPath(Color color) {
Direction moveDirection = Direction.findDirection(this.move);
int distance = this.move.traversedCells();
int stepIndex = move.getStart().toIndex();
for (int step = 0; step < distance; step++) {
stepIndex += moveDirection.getIndexOffset();
if (Coordinate.fromIndex(stepIndex).equals(move.getFinish())) {
Piece pieceDest = this.board.pieceAt(move.getFinish());
if (pieceDest == null)
return true;
return pieceDest.getColor() != color;
}
if (!this.board.isCellEmpty(Coordinate.fromIndex(stepIndex)))
return false;
}
return false;
}
}

View File

@@ -1,222 +0,0 @@
package chess.pgn;
import java.util.List;
import chess.controller.CommandExecutor;
import chess.controller.PlayerCommand;
import chess.controller.commands.CastlingCommand;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.GetPlayerMovesCommand;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.NewGameCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.event.EmptyGameDispatcher;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Game;
import chess.model.Move;
import chess.model.Piece;
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 GameAdaptator() {
// @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) {
GetPieceAtCommand cmd = new GetPieceAtCommand(coordinate);
commandExecutor.executeCommand(cmd);
return cmd.getPiece();
}
private static List<Move> playerMoves(CommandExecutor commandExecutor) {
GetPlayerMovesCommand cmd = new GetPlayerMovesCommand();
commandExecutor.executeCommand(cmd);
return cmd.getMoves();
}
private static String gameEnd(Game game) {
switch (game.checkGameStatus()) {
case Draw:
case Pat:
return "1/2-1/2";
case CheckMate:
if (game.getPlayerTurn() == Color.White)
return "1-0";
return "0-1";
default:
return "";
}
}
private static String resolveAmbiguity(CommandExecutor cmdExec, Move pieceMove) {
Piece movingPiece = pieceAt(cmdExec, pieceMove.getStart());
assert movingPiece != null;
if (movingPiece instanceof Pawn)
return "";
List<Move> moves = playerMoves(cmdExec);
for (Move move : moves) {
if (move.equals(pieceMove) || !move.getFinish().equals(pieceMove.getFinish()))
continue;
Piece otherPiece = pieceAt(cmdExec, move.getStart());
// checking type of piece
if (otherPiece.hashCode() != movingPiece.hashCode())
continue;
String startPos = toString(pieceMove.getStart());
if (move.getStart().getX() != pieceMove.getStart().getX())
// not on the same column
return Character.toString(startPos.charAt(0));
else
return Character.toString(startPos.charAt(1));
}
return "";
}
private static String capture(MoveCommand move, Piece movingPiece) {
String result = "";
if (move.getDeadPiece() != null) {
if (movingPiece instanceof Pawn) {
result += toString(move.getMove().getStart()).charAt(0);
}
result += "x";
}
return result;
}
private static String promote(PlayerCommand nextCommand) {
if (nextCommand != null && nextCommand instanceof PromoteCommand promoteCommand) {
String result = "=";
result += switch (promoteCommand.getPromoteType()) {
case Bishop -> "B";
case Knight -> "N";
case Queen -> "Q";
case Rook -> "R";
};
return result;
}
return "";
}
private static String castling(CastlingCommand castlingCommand) {
String result = "O-O";
if (castlingCommand.isBigCastling())
result += "-O";
return result;
}
private static String checkCheckMate(Game game) {
switch (game.checkGameStatus()) {
case CheckMate:
return "#";
case Check:
return "+";
default:
return "";
}
}
private static String printMove(PlayerCommand cmd, PlayerCommand nextCommand, Game virtualGame,
CommandExecutor executor) {
String result = "";
if (cmd instanceof MoveCommand move) {
Piece movingPiece = virtualGame.getBoard().pieceAt(move.getMove().getStart());
assert movingPiece != null;
// piece name
result += piecePgnName.visit(movingPiece);
// ambiguious start
result += resolveAmbiguity(executor, move.getMove());
// capture
result += capture(move, movingPiece);
// end cell
result += toString(move.getMove().getFinish());
// promote
result += promote(nextCommand);
} else if (cmd instanceof CastlingCommand castlingCommand) {
result += castling(castlingCommand);
}
executor.executeCommand(cmd);
// check or checkmate
result += checkCheckMate(virtualGame);
result += " ";
return result;
}
public static String exportGame(Game game) {
Game virtualGame = new Game();
CommandExecutor executor = new CommandExecutor(virtualGame, new EmptyGameDispatcher());
executor.executeCommand(new NewGameCommand());
List<PlayerCommand> commands = game.getMoves();
String result = "";
int tour = 1;
for (int i = 0; i < commands.size(); i++) {
PlayerCommand cmd = commands.get(i);
PlayerCommand nextCommand = null;
if (i != commands.size() - 1) {
nextCommand = commands.get(i + 1);
}
if (virtualGame.getPlayerTurn() == Color.White) {
result += tour + ".";
tour++;
}
result += printMove(cmd, nextCommand, virtualGame, executor);
}
return result + " " + gameEnd(virtualGame);
}
public static String toString(Coordinate coordinate) {
String letters = "abcdefgh";
String numbers = "87654321";
return Character.toString(letters.charAt(coordinate.getX())) + numbers.charAt(coordinate.getY());
}
}

View File

@@ -1,43 +0,0 @@
package chess.pgn;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class PiecePgnName implements PieceVisitor<String> {
@Override
public String visitPiece(Bishop bishop) {
return "B";
}
@Override
public String visitPiece(King king) {
return "K";
}
@Override
public String visitPiece(Knight knight) {
return "N";
}
@Override
public String visitPiece(Pawn pawn) {
return "";
}
@Override
public String visitPiece(Queen queen) {
return "Q";
}
@Override
public String visitPiece(Rook rook) {
return "R";
}
}

View File

@@ -1,31 +0,0 @@
package chess.simulator;
import chess.controller.CommandExecutor;
import chess.model.Coordinate;
import chess.model.Move;
import java.util.Arrays;
import java.util.List;
public class CastlingTest extends Simulator {
public CastlingTest(CommandExecutor commandExecutor) {
super(commandExecutor);
}
@Override
protected List<Move> getMoves() {
return Arrays.asList(
// white pawn
new Move(new Coordinate(6, 6), new Coordinate(6, 4)),
// black knight
new Move(new Coordinate(1, 0), new Coordinate(0, 2)),
// white bishop
new Move(new Coordinate(5, 7), new Coordinate(7, 5)),
// black pawn
new Move(new Coordinate(1, 1), new Coordinate(1, 2)),
// white knight
new Move(new Coordinate(6, 7), new Coordinate(5, 5)),
// black pawn, bis
new Move(new Coordinate(2, 1), new Coordinate(2, 2)));
}
}

View File

@@ -1,29 +0,0 @@
package chess.simulator;
import chess.controller.CommandExecutor;
import chess.model.Coordinate;
import chess.model.Move;
import java.util.Arrays;
import java.util.List;
public class EnPassantTest extends Simulator{
public EnPassantTest(CommandExecutor commandExecutor) {
super(commandExecutor);
}
@Override
protected List<Move> getMoves() {
return Arrays.asList(
// white pawn
new Move(new Coordinate(4, 6), new Coordinate(4, 4)),
// black pawn 1
new Move(new Coordinate(4, 1), new Coordinate(4, 2)),
// white pawn
new Move(new Coordinate(4, 4), new Coordinate(4, 3)),
// black pawn #2
new Move(new Coordinate(3, 1), new Coordinate(3, 3)));
}
}

View File

@@ -1,28 +0,0 @@
package chess.simulator;
import java.util.Arrays;
import java.util.List;
import chess.controller.CommandExecutor;
import chess.model.Coordinate;
import chess.model.Move;
public class FoolCheckMate extends Simulator {
public FoolCheckMate(CommandExecutor commandExecutor) {
super(commandExecutor);
}
@Override
public List<Move> getMoves() {
return Arrays.asList(
// white pawn
new Move(new Coordinate(5, 6), new Coordinate(5, 5)),
// black pawn
new Move(new Coordinate(4, 1), new Coordinate(4, 3)),
// 2nd white pawn
new Move(new Coordinate(6, 6), new Coordinate(6, 4)),
// black queen
new Move(new Coordinate(3, 0), new Coordinate(7, 4)));
}
}

View File

@@ -1,40 +0,0 @@
package chess.simulator;
import java.util.Arrays;
import java.util.List;
import chess.controller.CommandExecutor;
import chess.model.Coordinate;
import chess.model.Move;
public class PromoteTest extends Simulator{
public PromoteTest(CommandExecutor commandExecutor) {
super(commandExecutor);
}
@Override
protected List<Move> getMoves() {
return Arrays.asList(
// white pawn
new Move(new Coordinate(5, 6), new Coordinate(5, 4)),
// black pawn
new Move(new Coordinate(4, 1), new Coordinate(4, 3)),
// white pawn capture
new Move(new Coordinate(5, 4), new Coordinate(4, 3)),
// black king
new Move(new Coordinate(4, 0), new Coordinate(4, 1)),
// white pawn moves
new Move(new Coordinate(4, 3), new Coordinate(4, 2)),
// black king
new Move(new Coordinate(4, 1), new Coordinate(5, 2)),
// white pawn moves
new Move(new Coordinate(4, 2), new Coordinate(4, 1)),
// black king
new Move(new Coordinate(5, 2), new Coordinate(6, 2))
// white pawn moves
// new Move(new Coordinate(4, 1), new Coordinate(4, 0))
);
}
}

View File

@@ -1,39 +0,0 @@
package chess.simulator;
import java.util.List;
import chess.controller.CommandExecutor;
import chess.controller.commands.MoveCommand;
import chess.controller.event.GameAdaptator;
import chess.model.Move;
import common.Signal0;
public abstract class Simulator extends GameAdaptator {
protected final CommandExecutor commandExecutor;
public final Signal0 onComplete = new Signal0();
private int currentMove = 0;
public Simulator(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
}
@Override
public void onGameStart() {
for (Move move : getMoves()) {
this.commandExecutor.executeCommand(new MoveCommand(move));
}
}
@Override
public void onBoardUpdate() {
currentMove++;
if (currentMove == getMoves().size()) {
onComplete.emit();
}
}
protected abstract List<Move> getMoves();
}

View File

@@ -1,38 +0,0 @@
package chess.view;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class AssetManager {
private static final String gradleBase = "app/src/main/resources/";
public static InputStream getResource(String name) {
// we first search it in files
InputStream inputStream = getFileInputStream(name);
if (inputStream != null)
return inputStream;
inputStream = getFileInputStream(gradleBase + name);
if (inputStream != null)
return inputStream;
// then in the jar
return ClassLoader.getSystemResourceAsStream(name);
}
private static InputStream getFileInputStream(String path) {
File f = new File(path);
if (f.exists()) {
FileInputStream fis;
try {
fis = new FileInputStream(f);
return fis;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}

View File

@@ -1,83 +0,0 @@
package chess.view.DDDrender;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class Camera {
public static final float fov = 70.0f;
public static final float zNear = 0.01f;
public static final float zFar = 1000.0f;
private static final Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
private static final Vector3f center = new Vector3f(0.0f, 0.0f, 0.0f);
private final float distance = 1.5f;
private final float camHeight = 1.5f;
private float aspectRatio;
private float angle;
private Vector3f pos;
public Camera() {
this.pos = new Vector3f(0.0f, camHeight, 0.0f);
this.angle = 0.0f;
}
public void move(float x, float y) {
this.pos.x += x;
this.pos.y += y;
}
public void setRotateAngle(float angle) {
this.angle = angle;
updatePostion();
}
private void updatePostion() {
final float finalX = (float) Math.sin(angle);
final float finalZ = (float) -Math.cos(angle);
this.pos.set(distance * finalX, this.pos.get(1), distance * finalZ);
}
public float getRotateAngle() {
return angle;
}
public Vector3f getPos() {
return pos;
}
public float getFov() {
return fov;
}
public void setX(float x) {
this.pos.x = x;
}
public void setY(float y) {
this.pos.y = y;
}
public void setZ(float z) {
this.pos.z = z;
}
public void setPosition(Vector3f pos) {
this.pos = pos;
}
public void setAspectRatio(float aspectRatio) {
this.aspectRatio = aspectRatio;
}
public Matrix4f getPerspectiveMatrix() {
return new Matrix4f().perspective((float) (Math.toRadians(fov)), aspectRatio, zNear, zFar);
}
public Matrix4f getViewMatrix() {
return new Matrix4f().lookAt(pos, center, up);
}
}

View File

@@ -1,18 +0,0 @@
package chess.view.DDDrender;
import java.util.List;
import chess.view.DDDrender.opengl.VertexArray;
public class DDDModel {
private final List<VertexArray> vaos;
public DDDModel(List<VertexArray> vaos) {
this.vaos = vaos;
}
public List<VertexArray> getVaos() {
return vaos;
}
}

View File

@@ -1,16 +0,0 @@
package chess.view.DDDrender;
import org.joml.Vector2f;
import chess.model.Coordinate;
class DDDPlacement {
static public Vector2f coordinates_to_vector(Coordinate coo) {
return coordinates_to_vector(coo.getX(), coo.getY());
}
static public Vector2f coordinates_to_vector(float x, float y) {
return new Vector2f(1.0f - 0.125f - x * 0.250f, 1.0f - 0.125f - y * 0.250f);
}
}

View File

@@ -1,78 +0,0 @@
package chess.view.DDDrender;
import org.joml.Vector2f;
import org.joml.Vector3f;
import chess.controller.CommandExecutor;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.event.GameAdaptator;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Piece;
import chess.view.DDDrender.world.PieceEntity;
import chess.view.DDDrender.world.World;
public class DDDView extends GameAdaptator {
private static final Vector3f BLACK = new Vector3f(0.3f, 0.3f, 0.3f);
private static final Vector3f WHITE = new Vector3f(1.0f, 1.0f, 1.0f);
private final CommandExecutor commandExecutor;
private final Window window;
private final Renderer renderer;
private final World world;
public DDDView(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
this.renderer = new Renderer();
this.world = new World(new Camera());
this.window = new Window(this.renderer, this.world);
this.window.OnCellClick.connect(this::onCellClick);
}
// Invoked when a cell is clicked
private void onCellClick(Coordinate coordinate) {
}
private Piece pieceAt(Coordinate pos) {
GetPieceAtCommand cmd = new GetPieceAtCommand(pos);
this.commandExecutor.executeCommand(cmd);
return cmd.getPiece();
}
@Override
public void onGameStart() {
this.window.scheduleTask(() -> {
initBoard();
});
}
private void initBoard() {
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate pos = new Coordinate(i, j);
Piece piece = pieceAt(pos);
if (piece == null)
continue;
Vector2f pieceBoardPos = DDDPlacement.coordinates_to_vector(pos);
Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y());
this.world.addEntity(new PieceEntity(piece, pieceWorldPos,
piece.getColor() == Color.White ? WHITE : BLACK,
piece.getColor() == Color.White ? 0.0f : (float) Math.PI));
}
}
}
public void run() {
this.window.addRegularTask((delta) -> {
final float angle = 1f;
final Camera cam = this.world.getCamera();
cam.setRotateAngle(cam.getRotateAngle() + angle * delta);
});
this.window.run();
}
}

View File

@@ -1,62 +0,0 @@
package chess.view.DDDrender;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL30;
import chess.view.DDDrender.loader.BoardModelLoader;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.shader.BoardShader;
import chess.view.DDDrender.shader.PieceShader;
import chess.view.DDDrender.shader.ShaderProgram;
public class Renderer {
private BoardShader boardShader;
private PieceShader pieceShader;
private VertexArray boardVao;
public Renderer() {
this.boardShader = new BoardShader();
this.pieceShader = new PieceShader();
}
public void Init() {
boardShader.LoadShader();
pieceShader.LoadShader();
this.boardVao = BoardModelLoader.GetBoardModel();
}
public void Update(Camera cam) {
this.boardShader.Start();
this.boardShader.SetCamMatrix(cam);
this.pieceShader.Start();
this.pieceShader.SetCamMatrix(cam);
}
public void RenderBoard() {
RenderVao(this.boardShader, this.boardVao);
}
public void Render(DDDModel model, Vector3f color, Vector3f position, float rotation) {
this.pieceShader.Start();
this.pieceShader.setModelColor(color);
this.pieceShader.setModelTransform(new Matrix4f().translate(position).rotate(rotation, new Vector3f(0, 1, 0)));
Render(model);
}
public void Render(DDDModel model) {
for (int i = 0; i < model.getVaos().size(); i++) {
VertexArray vao = model.getVaos().get(i);
RenderVao(this.pieceShader, vao);
}
}
public void RenderVao(ShaderProgram shader, VertexArray vertexArray) {
shader.Start();
vertexArray.Bind();
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
vertexArray.Unbind();
}
}

View File

@@ -1,143 +0,0 @@
package chess.view.DDDrender.loader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.assimp.AIFace.Buffer;
import org.lwjgl.assimp.AIMesh;
import org.lwjgl.assimp.AINode;
import org.lwjgl.assimp.AIScene;
import org.lwjgl.assimp.AIVector3D;
import org.lwjgl.assimp.Assimp;
import org.lwjgl.system.MemoryUtil;
import chess.view.AssetManager;
import chess.view.DDDrender.DDDModel;
import chess.view.DDDrender.opengl.ElementBuffer;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.opengl.VertexBuffer;
public class ModelLoader {
private static final int VERTEX_SIZE = 3;
private static final int UV_SIZE = 2;
private static final int VERTEX_POSITION_INDEX = 0;
private static final int VERTEX_UV_INDEX = 1;
private static final int VERTEX_NORMAL_INDEX = 2;
private static float[] toFloatArray(List<Float> list) {
float[] result = new float[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private static int[] toIntArray(List<Integer> list) {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private static VertexArray processMesh(AIMesh mesh, AIScene scene) {
List<Float> positions = new ArrayList<>();
List<Float> textureCoords = new ArrayList<>();
List<Float> normals = new ArrayList<>();
List<Integer> indicies = new ArrayList<>();
Buffer faces = mesh.mFaces();
int faceNumber = mesh.mNumFaces();
for (int i = 0; i < faceNumber; i++) {
IntBuffer faceIndicies = faces.get(i).mIndices();
for (int j = 0; j < faceIndicies.capacity(); j++) {
indicies.add(faceIndicies.get(j));
}
}
int vertNumber = mesh.mNumVertices();
org.lwjgl.assimp.AIVector3D.Buffer vertecies = mesh.mVertices();
for (int i = 0; i < vertNumber; i++) {
AIVector3D vertex = vertecies.get(i);
positions.add(vertex.x());
positions.add(vertex.y());
positions.add(vertex.z());
}
org.lwjgl.assimp.AIVector3D.Buffer vertexNormals = mesh.mNormals();
for (int i = 0; i < vertNumber; i++) {
AIVector3D normal = vertexNormals.get(i);
normals.add(normal.x());
normals.add(normal.y());
normals.add(normal.z());
}
// PointerBuffer vertexTexture = mesh.mTextureCoords();
// for (int i = 0; i < vertNumber; i++) {
// PointerBuffer buff = mesh.mTextureCoords();
// textureCoords.add(buff.get(i).x());
// textureCoords.add(buff.get(i).y());
// }
VertexBuffer positionVBO = new VertexBuffer(toFloatArray(positions), VERTEX_SIZE);
positionVBO.AddVertexAttribPointer(VERTEX_POSITION_INDEX, VERTEX_SIZE, 0);
VertexBuffer textureVBO = new VertexBuffer(toFloatArray(textureCoords), UV_SIZE);
textureVBO.AddVertexAttribPointer(VERTEX_UV_INDEX, UV_SIZE, 0);
VertexBuffer normalVBO = new VertexBuffer(toFloatArray(normals), VERTEX_SIZE);
normalVBO.AddVertexAttribPointer(VERTEX_NORMAL_INDEX, VERTEX_SIZE, 0);
VertexArray vao = new VertexArray(new ElementBuffer(toIntArray(indicies)));
vao.Bind();
vao.BindVertexBuffer(positionVBO);
vao.BindVertexBuffer(textureVBO);
vao.BindVertexBuffer(normalVBO);
vao.Unbind();
return vao;
}
private static void processNode(AINode node, AIScene scene, List<VertexArray> meshes) {
for (int i = 0; i < node.mNumChildren(); i++) {
AINode child = AINode.create(node.mChildren().get(i));
processNode(child, scene, meshes);
}
for (int i = 0; i < node.mNumMeshes(); i++) {
AIMesh mesh = AIMesh.create(scene.mMeshes().get(node.mMeshes().get(i)));
meshes.add(processMesh(mesh, scene));
}
}
public static DDDModel loadModel(String filename) throws IOException {
InputStream input = AssetManager.getResource(filename);
byte[] buffer = input.readAllBytes();
ByteBuffer data = MemoryUtil.memCalloc(buffer.length);
data.put(buffer);
data.flip();
AIScene scene = Assimp.aiImportFileFromMemory(
data,
Assimp.aiProcess_Triangulate | Assimp.aiProcess_PreTransformVertices | Assimp.aiProcess_GlobalScale
| Assimp.aiProcess_ValidateDataStructure,
"");
if (scene == null)
System.err.println(Assimp.aiGetErrorString());
List<VertexArray> vertecies = new ArrayList<>();
processNode(scene.mRootNode(), scene, vertecies);
MemoryUtil.memFree(data);
return new DDDModel(vertecies);
}
}

View File

@@ -1,75 +0,0 @@
package chess.view.DDDrender.loader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
import chess.view.DDDrender.DDDModel;
public class Piece3DModel implements PieceVisitor<String> {
private static final String basePath = "3d/";
private static final Map<String, DDDModel> cache = new HashMap<>();
public DDDModel getModel(Piece piece) throws IOException {
if (piece == null)
return null;
String path = basePath + colorToString(piece.getColor()) + "-" + visit(piece) + ".fbx";
return getModel(path);
}
private DDDModel getModel(String path) throws IOException {
DDDModel model = cache.get(path);
if (model != null)
return model;
model = ModelLoader.loadModel(path);
cache.put(path, model);
return model;
}
private String colorToString(Color color) {
return color == Color.Black ? "black" : "white";
}
@Override
public String visitPiece(Bishop bishop) {
return "bishop";
}
@Override
public String visitPiece(King king) {
return "king";
}
@Override
public String visitPiece(Knight knight) {
return "knight";
}
@Override
public String visitPiece(Pawn pawn) {
return "pawn";
}
@Override
public String visitPiece(Queen queen) {
return "queen";
}
@Override
public String visitPiece(Rook rook) {
return "rook";
}
}

View File

@@ -1,5 +0,0 @@
package chess.view.DDDrender.opengl;
public record VertexAttribPointer(int index, int size, int offset) {
}

View File

@@ -1,102 +0,0 @@
package chess.view.DDDrender.shader;
import chess.view.DDDrender.Camera;
public class BoardShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 lightPosition = vec3(0, 10, 0);
flat out vec3 pass_color;
out vec3 toLightVector;
out vec3 toCameraVector;
out vec3 surfaceNormal;
void main(void){
const vec4 normal = vec4(0.0, 1.0, 0.0, 1.0);
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
toLightVector = lightPosition - position;
toCameraVector = camPos - position;
surfaceNormal = (normal).xyz;
pass_color = color;
}
""";
private static String fragmentShader = """
#version 330
flat in vec3 pass_color;
in vec3 toLightVector;
in vec3 toCameraVector;
in vec3 surfaceNormal;
out vec4 out_color;
void main(void){
const float shineDamper = 10.0;
const float reflectivity = 1.0;
float lightDistance = length(toLightVector);
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
vec3 unitNormal = normalize(surfaceNormal);
vec3 unitLightVector = normalize(toLightVector);
vec3 unitCamVector = normalize(toCameraVector);
vec3 lightDirection = -unitLightVector;
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
float specular = pow(specularFactor, shineDamper) * reflectivity;
float brightness = (diffuse + specular) / attenuationFactor;
out_color = brightness * vec4(pass_color, 1.0);
out_color.w = 1.0;
}
""";
private int location_ProjectionMatrix = 0;
private int location_ViewMatrix = 0;
public BoardShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
location_ViewMatrix = GetUniformLocation("viewMatrix");
}
public void SetCamMatrix(Camera camera) {
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
}
}

View File

@@ -1,117 +0,0 @@
package chess.view.DDDrender.shader;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import chess.view.DDDrender.Camera;
public class PieceShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 uv;
layout(location = 2) in vec3 normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelTransform;
uniform vec3 lightPosition = vec3(0, 10, 0);
out vec3 toLightVector;
out vec3 toCameraVector;
out vec3 surfaceNormal;
void main(void){
vec4 modelPos = modelTransform * vec4(position, 1.0);
vec4 globalNormal = modelTransform * vec4(normal, 1.0);
gl_Position = projectionMatrix * viewMatrix * modelPos;
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
toLightVector = lightPosition - modelPos.xyz;
toCameraVector = camPos - position;
surfaceNormal = normalize(globalNormal.xyz);
}
""";
private static String fragmentShader = """
#version 330
in vec3 toLightVector;
in vec3 toCameraVector;
in vec3 surfaceNormal;
uniform vec3 modelColor;
out vec4 out_color;
void main(void){
const float shineDamper = 10.0;
const float reflectivity = 1.0;
float lightDistance = length(toLightVector);
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
vec3 unitNormal = normalize(surfaceNormal);
vec3 unitLightVector = normalize(toLightVector);
vec3 unitCamVector = normalize(toCameraVector);
vec3 lightDirection = -unitLightVector;
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
float specular = pow(specularFactor, shineDamper) * reflectivity;
float brightness = (diffuse + specular) / attenuationFactor;
out_color = brightness * vec4(modelColor, 1.0);
out_color.w = 1.0;
}
""";
private int location_ProjectionMatrix = 0;
private int location_ViewMatrix = 0;
private int location_ModelTransform = 0;
private int location_ModelColor = 0;
public PieceShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
location_ViewMatrix = GetUniformLocation("viewMatrix");
location_ModelTransform = GetUniformLocation("modelTransform");
location_ModelColor = GetUniformLocation("modelColor");
}
public void SetCamMatrix(Camera camera) {
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
}
public void setModelTransform(Matrix4f mat) {
LoadMat4(location_ModelTransform, mat);
}
public void setModelColor(Vector3f color) {
LoadVector(location_ModelColor, color);
}
}

View File

@@ -1,9 +0,0 @@
package chess.view.DDDrender.world;
import chess.view.DDDrender.Renderer;
public abstract class Entity {
public abstract void render(Renderer renderer);
}

View File

@@ -1,55 +0,0 @@
package chess.view.DDDrender.world;
import java.io.IOException;
import org.joml.Vector3f;
import chess.model.Piece;
import chess.view.DDDrender.DDDModel;
import chess.view.DDDrender.Renderer;
import chess.view.DDDrender.loader.Piece3DModel;
public class PieceEntity extends Entity {
private static final Piece3DModel modelLoader = new Piece3DModel();
private final Piece piece;
private Vector3f position;
private Vector3f color;
private float rotation;
private DDDModel model;
public PieceEntity(Piece piece, Vector3f position, Vector3f color, float rotation) {
this.piece = piece;
this.position = position;
this.color = color;
this.rotation = rotation;
try {
this.model = modelLoader.getModel(piece);
} catch (IOException e) {
e.printStackTrace();
}
}
public Piece getPiece() {
return piece;
}
@Override
public void render(Renderer renderer) {
renderer.Render(model, color, position, rotation);
}
public void setPosition(Vector3f position) {
this.position = position;
}
public void setColor(Vector3f color) {
this.color = color;
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
}

View File

@@ -1,40 +0,0 @@
package chess.view.DDDrender.world;
import java.util.ArrayList;
import java.util.List;
import chess.model.Piece;
import chess.view.DDDrender.Camera;
public class World {
private final Camera camera;
private final List<Entity> entites;
public World(Camera camera) {
this.camera = camera;
this.entites = new ArrayList<>();
}
public PieceEntity getEntity(Piece piece) {
for (Entity entity : entites) {
if (entity instanceof PieceEntity p) {
if (p.getPiece() == piece)
return p;
}
}
return null;
}
public void addEntity(Entity entity) {
this.entites.add(entity);
}
public Camera getCamera() {
return camera;
}
public List<Entity> getEntites() {
return entites;
}
}

View File

@@ -1,38 +0,0 @@
package chess.view.consolerender;
public class Colors {
// Reset
public static final String RESET = "\u001B[0m"; // Text Reset
// Regular Colors
public static final String BLACK = "\033[38;2;0;0;0m";
public static final String WHITE = "\033[38;2;255;255;255m";
public static final String RED = "\033[38;2;255;0;0m";
public static final String GREEN = "\033[38;2;0;255;0m";
public static final String YELLOW = "\033[38;2;255;255;0m";
public static final String BLUE = "\033[38;2;0;0;255m";
public static final String PURPLE = "\033[38;2;255;0;255m";
public static final String CYAN = "\033[38;2;0;255;255m";
// Background
public static final String BLACK_BACKGROUND = "\033[40m"; // BLACK
public static final String LIGHT_GRAY_BACKGROUND = "\033[48;2;180;180;180m";
public static final String DARK_GRAY_BACKGROUND = "\033[48;2;120;120;120m";
public static final String RED_BACKGROUND = "\033[41m"; // RED
public static final String GREEN_BACKGROUND = "\033[42m"; // GREEN
public static final String YELLOW_BACKGROUND = "\033[43m"; // YELLOW
public static final String BLUE_BACKGROUND = "\033[44m"; // BLUE
public static final String PURPLE_BACKGROUND = "\033[45m"; // PURPLE
public static final String CYAN_BACKGROUND = "\033[46m"; // CYAN
public static final String WHITE_BACKGROUND = "\033[47m"; // WHITE
// High Intensity backgrounds
public static final String BLACK_BACKGROUND_BRIGHT = "\033[0;100m";// BLACK
public static final String RED_BACKGROUND_BRIGHT = "\033[0;101m";// RED
public static final String GREEN_BACKGROUND_BRIGHT = "\033[0;102m";// GREEN
public static final String YELLOW_BACKGROUND_BRIGHT = "\033[0;103m";// YELLOW
public static final String BLUE_BACKGROUND_BRIGHT = "\033[0;104m";// BLUE
public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
}

View File

@@ -1,336 +0,0 @@
package chess.view.consolerender;
import chess.controller.Command;
import chess.controller.CommandExecutor;
import chess.controller.commands.*;
import chess.controller.commands.PromoteCommand.PromoteType;
import chess.controller.event.GameListener;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Console implements GameListener {
private final Scanner scanner = new Scanner(System.in);
private final CommandExecutor commandExecutor;
private final ConsolePieceName consolePieceName = new ConsolePieceName();
private boolean captureInput;
private final ExecutorService executor;
public Console(CommandExecutor commandExecutor, boolean captureInput) {
this.commandExecutor = commandExecutor;
this.executor = Executors.newSingleThreadExecutor();
this.captureInput = captureInput;
}
public Console(CommandExecutor commandExecutor) {
this(commandExecutor, true);
}
private Piece pieceAt(int x, int y) {
return pieceAt(new Coordinate(x, y));
}
private Command.CommandResult sendCommand(Command command) {
return this.commandExecutor.executeCommand(command);
}
private Piece pieceAt(Coordinate coordinate) {
GetPieceAtCommand command = new GetPieceAtCommand(coordinate);
sendCommand(command);
return command.getPiece();
}
public Coordinate stringToCoordinate(String coordinates) throws Exception {
char xPos = coordinates.charAt(0);
char yPos = coordinates.charAt(1);
int x;
if (xPos >= 'A' && xPos <= 'Z') {
x = xPos - 'A';
} else if (xPos >= 'a' && xPos <= 'z') {
x = xPos - 'a';
} else {
throw new Exception("Invalid input");
}
if (!(yPos >= '1' && yPos <= '9')) {
throw new Exception("Invalid input");
}
int y = Coordinate.VALUE_MAX - 1 - (yPos - '1');
return new Coordinate(x, y);
}
@Override
public void onPlayerTurn(Color color, boolean undone) {
if (!captureInput)
return;
System.out.println(Colors.RED + "Player turn: " + color + Colors.RESET);
this.executor.submit(() -> {
boolean endTurn;
String line = "0";
do {
if (!line.isEmpty()) {
System.out.println("""
Pick your choice:
1 - Move
2 - Show potential moves
3 - Surrender
""");
System.out.flush();
}
line = scanner.nextLine();
endTurn = switch (line) {
case "1" -> playerPickedMove(color);
case "2" -> playerPickedShowMoves(color);
case "3" -> playerPickedSurrender(color);
default -> false;
};
} while (!endTurn);
});
}
private boolean playerPickedSurrender(Color color) {
sendCommand(new SurrenderCommand(color));
return true;
}
public boolean playerPickedMove(Color color) {
try {
System.out.println("Piece to move, or \"castling\" for a castling");
String answer = scanner.nextLine();
if (answer.equalsIgnoreCase("castling")) {
return onAskedCastling();
}
Coordinate start = stringToCoordinate(answer);
System.out.println("New position: ");
Coordinate end = stringToCoordinate(scanner.nextLine());
Command.CommandResult result = sendCommand(new MoveCommand(new Move(start, end)));
return switch (Objects.requireNonNull(result)) {
case Command.CommandResult.Moved, Command.CommandResult.ActionNeeded -> true;
default -> false;
};
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
private boolean playerPickedShowMoves(Color color) {
try {
System.out.println("Piece to examine: ");
Coordinate piece = stringToCoordinate(scanner.nextLine());
GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(piece);
if (sendCommand(movesCommand) == Command.CommandResult.NotAllowed) {
System.out.println("Not allowed.");
return false;
}
List<Coordinate> allowedMoves = movesCommand.getDestinations();
if (allowedMoves.isEmpty()) {
System.out.println("No moves allowed for this piece.");
return false;
}
displayMoves(piece, allowedMoves);
return false;
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
@Override
public void onWin(Color color) {
System.out.println(Colors.RED + "Victory of player " + color + Colors.RESET);
}
@Override
public void onKingInCheck() {
System.out.println(Colors.RED + "Check!" + Colors.RESET);
}
@Override
public void onKingInMat() {
System.out.println(Colors.RED + "Checkmate!" + Colors.RESET);
}
@Override
public void onPatSituation() {
System.out.println("Pat! It's a draw!");
}
@Override
public void onSurrender(Color color) {
System.out.println("The " + color + " player has surrendered!");
}
@Override
public void onGameStart() {
System.out.println("Game start!");
onBoardUpdate();
}
@Override
public void onGameEnd() {
System.out.println("Thank you for playing!");
this.commandExecutor.close();
this.executor.shutdown();
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
System.out.println("The pawn on the " + pieceCoords + " coordinates needs to be promoted.");
System.out.println("Enter 'B' to promote it into a Bishop, 'N' for a Knight, 'Q' for a Queen, 'R' for a Rook.");
System.out.flush();
this.executor.submit(() -> {
PromoteType newPiece;
boolean valid = false;
do {
try {
String promotion = scanner.next();
newPiece = switch (promotion) {
case "B", "b", "Bishop", "bishop" -> PromoteType.Bishop;
case "N", "n", "Knight", "knight" -> PromoteType.Knight;
case "Q", "q", "Queen", "queen" -> PromoteType.Queen;
case "R", "r", "Rook", "rook" -> PromoteType.Rook;
default -> throw new Exception();
};
valid = true;
sendCommand(new PromoteCommand(newPiece));
} catch (Exception e) {
System.out.println("Invalid input!");
}
} while (!valid);
});
}
@Override
public void onBoardUpdate() {
if (!this.captureInput)
return;
StringBuilder string = new StringBuilder();
string.append(" a b c d e f g h \n");
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
string.append(8 - i).append(" ");
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Piece p = pieceAt(j, i);
if ((i + j) % 2 == 0) {
string.append(Colors.LIGHT_GRAY_BACKGROUND);
} else {
string.append(Colors.DARK_GRAY_BACKGROUND);
}
if (p == null) {
string.append(" " + Colors.RESET);
} else {
string.append(" ").append(consolePieceName.getString(p)).append(" ").append(Colors.RESET);
}
}
string.append("\n");
}
System.out.println(string);
System.out.flush();
}
public void displayMoves(Coordinate piece, List<Coordinate> moves) {
StringBuilder string = new StringBuilder();
string.append(" a b c d e f g h \n");
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
string.append(8 - i).append(" ");
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate currentCell = new Coordinate(j, i);
Piece p = pieceAt(j, i);
if (moves.contains(currentCell)) {
string.append(Colors.YELLOW_BACKGROUND);
} else {
if ((i + j) % 2 == 0) {
string.append(Colors.LIGHT_GRAY_BACKGROUND);
} else {
string.append(Colors.DARK_GRAY_BACKGROUND);
}
}
if (p == null) {
string.append(" " + Colors.RESET);
} else {
if (currentCell.equals(piece)) {
string.append(Colors.RED_BACKGROUND).append(" ").append(consolePieceName.getString(p))
.append(" ").append(Colors.RESET);
} else {
string.append(" ").append(consolePieceName.getString(p)).append(" ").append(Colors.RESET);
}
}
}
string.append("\n");
}
System.out.println(string);
}
@Override
public void onMove(Move move) {
}
@Override
public void onMoveNotAllowed(Move move) {
System.out.println("Move not allowed.");
}
@Override
public void onDraw() {
System.out.println("Repeated positions!");
}
private boolean onAskedCastling() {
GetAllowedCastlingsCommand cmd = new GetAllowedCastlingsCommand();
sendCommand(cmd);
return switch (cmd.getCastlingResult()) {
case Small -> onSmallCastling();
case Big -> onBigCastling();
case Both -> onBothCastling();
default -> {
System.out.println("No castling allowed.");
yield false;
}
};
}
private boolean onSmallCastling() {
System.out.println("Small castling allowed. Confirm with \"y\":");
String answer = scanner.nextLine();
if (!(answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes"))) {
return false;
} else {
return (commandExecutor.executeCommand(new CastlingCommand(false)) != Command.CommandResult.Moved);
}
}
private boolean onBigCastling() {
System.out.println("Big castling allowed. Confirm with \"y\":");
String answer = scanner.nextLine();
if (!(answer.equalsIgnoreCase("y") || answer.equalsIgnoreCase("yes"))) {
return false;
} else {
return (commandExecutor.executeCommand(new CastlingCommand(true)) != Command.CommandResult.Moved);
}
}
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();
return switch (answer) {
case "s", "S", "small", "Small", "castling", "normal", "Normal" ->
(commandExecutor.executeCommand(new CastlingCommand(false)) != Command.CommandResult.Moved);
case "b", "B", "big", "Big", "big castling", "Big castling" ->
(commandExecutor.executeCommand(new CastlingCommand(true)) != Command.CommandResult.Moved);
default -> false;
};
}
public void setCaptureInput(boolean captureInput) {
this.captureInput = captureInput;
}
}

View File

@@ -1,48 +0,0 @@
package chess.view.consolerender;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.*;
public class ConsolePieceName implements PieceVisitor<String> {
public String getString(Piece piece){
if (piece.getColor()== Color.Black){
return Colors.BLACK + visit(piece);
}
else {
return Colors.WHITE + visit(piece);
}
}
@Override
public String visitPiece(Bishop bishop) {
return "B";
}
@Override
public String visitPiece(King king) {
return "K";
}
@Override
public String visitPiece(Knight knight) {
return "N";
}
@Override
public String visitPiece(Pawn pawn) {
return "P";
}
@Override
public String visitPiece(Queen queen) {
return "Q";
}
@Override
public String visitPiece(Rook rook) {
return "R";
}
}

View File

@@ -0,0 +1,61 @@
package chess.view.render2D;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.*;
public class PieceFileName implements PieceVisitor {
private String pieceName;
private static final String BASE = "app/src/main/resources/pieces2D/";
PieceFileName(Piece piece) {
visit(piece);
pieceName = colorToString(piece.getColor()) +"-"+ pieceName;
}
private String colorToString(int color) {
switch (color) {
case 0:
return "white";
case 1:
return "black";
default:
return "";
}
}
@Override
public void visitPiece(Bishop p) {
this.pieceName = "bishop";
}
@Override
public void visitPiece(Knight p) {
this.pieceName = "knight";
}
@Override
public void visitPiece(Pawn p) {
this.pieceName = "pawn";
}
@Override
public void visitPiece(Queen p) {
this.pieceName = "queen";
}
@Override
public void visitPiece(King p) {
this.pieceName = "king";
}
@Override
public void visitPiece(Rook p) {
this.pieceName = "rook";
}
public String getFileName() {
return BASE + pieceName + ".png";
}
}

View File

@@ -0,0 +1,23 @@
package chess.view.render2D;
import javax.swing.*;
import java.awt.*;
public enum Pieces {
WHITE_KING,
WHITE_QUEEN,
WHITE_KNIGHT,
WHITE_BISHOP,
WHITE_ROOK,
WHITE_PAWN,
BLACK_KING,
BLACK_QUEEN,
BLACK_KNIGHT,
BLACK_BISHOP,
BLACK_ROOK,
BLACK_PAWN;
public static ImageIcon getIcon(String path) {
return new ImageIcon(new ImageIcon(path).getImage().getScaledInstance(100,100, Image.SCALE_SMOOTH));
}
}

View File

@@ -0,0 +1,105 @@
package chess.view.render2D;
import chess.controller.Controller;
import chess.model.Coordinates;
import chess.model.Game;
import chess.model.Piece;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;
public class Window extends JFrame implements Observer {
private final Game game;
private final Controller controller;
private final JLabel[][] tab;
private static final String path = "app/src/main/resources/pieces2D/";
Coordinates mouseClick = null;
Coordinates mouseRelease = null;
public Window(Game game) {
this.game = game;
game.addObserver(this);
this.controller = new Controller(game, this);
tab = new JLabel[game.getSize()][game.getSize()];
build();
showBoard();
}
public static void main(String[] args){
Game game = new Game();
Thread gameThread = new Thread(game);
gameThread.start();
Window f = new Window(game);
f.setVisible(true);
}
/**
* Create a playing grid with alternating black & white cells
*/
private void build(){
JPanel jp = new JPanel(new GridLayout(game.getSize(), game.getSize()));
setContentPane(jp);
setTitle("Let's play chess!");
for (int y = 0; y < game.getSize(); y++){
for (int x = 0; x < game.getSize(); x++){
JLabel jl = new JLabel();
jl.setOpaque(true);
if (((y+x)%2)!=0) {
jl.setBackground(Color.BLACK);
}
final int yy = y;
final int xx = x;
jl.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (mouseClick == null) {
mouseClick = new Coordinates(xx, yy);
}
else {
mouseRelease = new Coordinates(xx, yy);
if (!mouseClick.equals(mouseRelease)) {
controller.sendMove(mouseClick, mouseRelease);
update();
}
mouseClick = null;
mouseRelease = null;
}
}
});
this.tab[x][y]=jl;
jp.add(jl);
}
}
setSize(800,800);
}
public void showBoard(){
for (int y = 0; y < game.getSize(); y++){
for (int x = 0; x < game.getSize(); x++){
Piece p = game.getBoard().getCell(new Coordinates(x,y)).getPiece();
if (p != null){
this.tab[x][y].setIcon(Pieces.getIcon(new PieceFileName(p).getFileName()));
}
else
{
this.tab[x][y].setIcon(null);
}
}
}
}
public void update() {
// todo
showBoard();
}
@Override
public void update(Observable o, Object arg) {
update();
}
}

View File

@@ -0,0 +1,128 @@
package chess.view.render3D;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class Camera {
public static final float fov = 70.0f;
// should be changed to match screen
public static final float aspect = 1.0f;
public static final float zNear = 0.01f;
public static final float zFar = 100.0f;
private Vector3f pos;
private float yaw = 0.0f;
private float pitch = 0.0f;
public Camera() {
this.pos = new Vector3f(0, 2.0f, 0);
setRotation(0.0f, -3.14150f / 2.0f);
}
public void move(float x, float y) {
this.pos.x += x;
this.pos.y += y;
}
public void rotate(float yaw, float pitch) {
this.yaw += yaw;
this.pitch += pitch;
}
public Vector3f getPos() {
return pos;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
public float getFov() {
return fov;
}
public void setX(float x) {
this.pos.x = x;
}
public void setY(float y) {
this.pos.y = y;
}
public void setZ(float z) {
this.pos.z = z;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public void reset() {
resetPosition();
resetRotation();
}
public void resetPosition() {
pos = new Vector3f(0.0f, 0.0f, 0.0f);
}
public void resetRotation() {
yaw = 0.0f;
pitch = 0.0f;
}
public void moveForward(float distance) {
pos.x += distance * (float) Math.cos(yaw);
pos.y += distance * (float) Math.sin(yaw);
}
public void moveRight(float distance) {
pos.x += distance * (float) Math.cos(yaw);
pos.y += distance * (float) Math.sin(yaw);
}
public void moveUp(float distance) {
pos.z += distance;
}
public void moveDown(float distance) {
pos.z -= distance;
}
public void addYaw(float angle) {
yaw += angle;
}
public void addPitch(float angle) {
pitch += angle;
}
public void setPosition(Vector3f pos) {
this.pos = pos;
}
public void setRotation(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
public Matrix4f getMatrix() {
Vector3f forward = new Vector3f(
(float) (Math.cos(yaw) * Math.cos(pitch)),
(float) (Math.sin(pitch)),
(float) (Math.sin(yaw) * Math.cos(pitch)));
return new Matrix4f()
.perspective((float) (Math.toRadians(fov)), aspect, zNear, zFar)
.lookAt(pos, forward, new Vector3f(0.0f, 1.0f, 0.0f));
}
}

View File

@@ -1,4 +1,4 @@
package chess.view.DDDrender.opengl; package chess.view.render3D;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;

View File

@@ -1,19 +1,30 @@
package chess.view.DDDrender.loader; package chess.view.render3D;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.opengl.*;
import chess.view.DDDrender.opengl.ElementBuffer; import static org.lwjgl.opengl.GL30.*;
import chess.view.DDDrender.opengl.VertexArray; import chess.view.render3D.shader.BoardShader;
import chess.view.DDDrender.opengl.VertexBuffer;
public class BoardModelLoader { public class Renderer {
private BoardShader shader;
private VertexArray vao;
private static int BOARD_WIDTH = 8; private static int BOARD_WIDTH = 8;
private static int BOARD_HEIGHT = 8; private static int BOARD_HEIGHT = 8;
private static int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT; private static int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT;
private static int SQUARE_VERTEX_COUNT = 4; private static int SQUARE_VERTEX_COUNT = 4;
private static float[] GetBoardPositions() { public Renderer() {
this.shader = new BoardShader();
}
public void Init() {
shader.LoadShader();
InitBoard();
}
private float[] GetBoardPositions() {
float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3]; float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int i = 0; i < BOARD_WIDTH; i++) { for (int i = 0; i < BOARD_WIDTH; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) { for (int j = 0; j < BOARD_HEIGHT; j++) {
@@ -47,12 +58,12 @@ public class BoardModelLoader {
return positions; return positions;
} }
private static float[] GetBoardColors() { private float[] GetBoardColors() {
float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3]; float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int i = 0; i < BOARD_WIDTH; i++) { for (int i = 0; i < BOARD_WIDTH; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) { for (int j = 0; j < BOARD_HEIGHT; j++) {
Vector3f color; Vector3f color;
if ((i + j) % 2 == 0) { if ((i + j) % 2 != 0) {
color = new Vector3f(1.0f, 1.0f, 1.0f); color = new Vector3f(1.0f, 1.0f, 1.0f);
} else { } else {
color = new Vector3f(0.0f, 0.0f, 0.0f); color = new Vector3f(0.0f, 0.0f, 0.0f);
@@ -68,12 +79,12 @@ public class BoardModelLoader {
return colors; return colors;
} }
private static int[] GetBoardIndicies() { private int[] GetBoardIndicies() {
int[] indices = new int[BOARD_SIZE * 6]; int[] indices = new int[BOARD_SIZE * 6];
for (int i = 0; i < BOARD_SIZE; i++) { for (int i = 0; i < BOARD_SIZE; i++) {
indices[i * 6] = i * 4; indices[i * 6] = i * 4;
indices[i * 6 + 1] = i * 4 + 2; indices[i * 6 + 1] = i * 4 + 1;
indices[i * 6 + 2] = i * 4 + 1; indices[i * 6 + 2] = i * 4 + 2;
indices[i * 6 + 3] = i * 4 + 1; indices[i * 6 + 3] = i * 4 + 1;
indices[i * 6 + 4] = i * 4 + 2; indices[i * 6 + 4] = i * 4 + 2;
indices[i * 6 + 5] = i * 4 + 3; indices[i * 6 + 5] = i * 4 + 3;
@@ -81,9 +92,9 @@ public class BoardModelLoader {
return indices; return indices;
} }
public static VertexArray GetBoardModel() { private void InitBoard() {
ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies()); ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies());
VertexArray vao = new VertexArray(eBuffer); this.vao = new VertexArray(eBuffer);
VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3); VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3);
positionBuffer.AddVertexAttribPointer(0, 3, 0); positionBuffer.AddVertexAttribPointer(0, 3, 0);
@@ -91,10 +102,22 @@ public class BoardModelLoader {
VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3); VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3);
colorBuffer.AddVertexAttribPointer(1, 3, 0); colorBuffer.AddVertexAttribPointer(1, 3, 0);
vao.Bind(); this.vao.Bind();
vao.BindVertexBuffer(positionBuffer); this.vao.BindVertexBuffer(positionBuffer);
vao.BindVertexBuffer(colorBuffer); this.vao.BindVertexBuffer(colorBuffer);
vao.Unbind(); this.vao.Unbind();
return vao; }
public void Render(Camera cam) {
this.shader.Start();
this.shader.SetCamMatrix(cam.getMatrix());
RenderVao(vao);
}
public void RenderVao(VertexArray vertexArray) {
this.shader.Start();
vertexArray.Bind();
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
vertexArray.Unbind();
} }
} }

View File

@@ -1,4 +1,4 @@
package chess.view.DDDrender.opengl; package chess.view.render3D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@@ -0,0 +1,13 @@
package chess.view.render3D;
public class VertexAttribPointer {
public int index;
public int size;
public int offset;
public VertexAttribPointer(int index, int size, int offset) {
this.index = index;
this.size = size;
this.offset = offset;
}
}

View File

@@ -1,4 +1,4 @@
package chess.view.DDDrender.opengl; package chess.view.render3D;
import static org.lwjgl.opengl.GL11.GL_FLOAT; import static org.lwjgl.opengl.GL11.GL_FLOAT;
@@ -41,9 +41,9 @@ public class VertexBuffer {
public void BindVertexAttribs() { public void BindVertexAttribs() {
for (VertexAttribPointer vertexAttribPointer : vertexAttribs) { for (VertexAttribPointer vertexAttribPointer : vertexAttribs) {
GL30.glEnableVertexAttribArray(vertexAttribPointer.index()); GL30.glEnableVertexAttribArray(vertexAttribPointer.index);
GL30.glVertexAttribPointer(vertexAttribPointer.index(), vertexAttribPointer.size(), GL_FLOAT, false, GL30.glVertexAttribPointer(vertexAttribPointer.index, vertexAttribPointer.size, GL_FLOAT, false,
this.dataStride * 4, vertexAttribPointer.offset()); this.dataStride * 4, vertexAttribPointer.offset);
} }
} }
} }

View File

@@ -1,21 +1,11 @@
package chess.view.DDDrender; package chess.view.render3D;
import org.lwjgl.*; import org.lwjgl.*;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*; import org.lwjgl.opengl.*;
import org.lwjgl.system.*; import org.lwjgl.system.*;
import chess.model.Coordinate;
import chess.view.DDDrender.world.Entity;
import chess.view.DDDrender.world.World;
import common.Signal1;
import java.nio.*; import java.nio.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Consumer;
import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
@@ -29,32 +19,11 @@ public class Window {
private long window; private long window;
private Renderer renderer; private Renderer renderer;
private final Camera cam; private Camera cam;
private final World world;
private final Queue<Runnable> tasks; public Window() {
private final List<Consumer<Float>> regularTasks;
public final Signal1<Coordinate> OnCellClick = new Signal1<>();
public Window(Renderer renderer, World world) {
this.renderer = new Renderer(); this.renderer = new Renderer();
this.cam = world.getCamera(); this.cam = new Camera();
this.tasks = new ConcurrentLinkedDeque<>();
this.world = world;
this.regularTasks = new ArrayList<>();
}
public void addRegularTask(Consumer<Float> task) {
this.regularTasks.add(task);
}
public synchronized void scheduleTask(Runnable runnable) {
this.tasks.add(runnable);
}
public synchronized Runnable getNextTask() {
return this.tasks.poll();
} }
public void run() { public void run() {
@@ -87,7 +56,7 @@ public class Window {
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
// Create the window // Create the window
window = glfwCreateWindow(1000, 1000, "3DChess", NULL, NULL); window = glfwCreateWindow(1000, 1000, "Chess4J", NULL, NULL);
if (window == NULL) if (window == NULL)
throw new RuntimeException("Failed to create the GLFW window"); throw new RuntimeException("Failed to create the GLFW window");
@@ -118,29 +87,9 @@ public class Window {
glfwShowWindow(window); glfwShowWindow(window);
} }
private void render(float delta, float aspectRatio) { private void render() {
cam.setAspectRatio(aspectRatio); cam.rotate(0.01f, 0.01f);
renderer.Update(cam); renderer.Render(cam);
renderer.RenderBoard();
renderWorld();
// renderPieces();
}
private void renderWorld() {
for (Entity entity : this.world.getEntites()) {
entity.render(this.renderer);
}
}
private void executeTasks(float delta) {
Runnable task = getNextTask();
while (task != null) {
task.run();
task = getNextTask();
}
for (Consumer<Float> consumer : regularTasks) {
consumer.accept(delta);
}
} }
private void loop() { private void loop() {
@@ -154,31 +103,16 @@ public class Window {
renderer.Init(); renderer.Init();
// Set the clear color // Set the clear color
glClearColor(0.4f, 0.4f, 0.6f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glFrontFace(GL_CW);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f); glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
double lastTime = glfwGetTime();
int width[] = new int[1];
int height[] = new int[1];
glfwGetWindowSize(window, width, height);
// Run the rendering loop until the user has attempted to close // Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key. // the window or has pressed the ESCAPE key.
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
double currentTime = glfwGetTime(); render();
float deltaTime = (float) (currentTime - lastTime);
render(deltaTime, (float) width[0] / (float) height[0]);
lastTime = glfwGetTime();
glfwSwapBuffers(window); // swap the color buffers glfwSwapBuffers(window); // swap the color buffers
@@ -186,11 +120,15 @@ public class Window {
// invoked during this call. // invoked during this call.
glfwPollEvents(); glfwPollEvents();
executeTasks(deltaTime); try (MemoryStack stack = stackPush()) {
IntBuffer pWidth = stack.mallocInt(1); // int*
IntBuffer pHeight = stack.mallocInt(1); // int*
glfwGetWindowSize(window, width, height); // Get the window size passed to glfwCreateWindow
glViewport(0, 0, width[0], height[0]); glfwGetWindowSize(window, pWidth, pHeight);
glViewport(0, 0, pWidth.get(), pHeight.get());
} // the stack frame is popped automatically
} }
} }

View File

@@ -0,0 +1,53 @@
package chess.view.render3D.shader;
import org.joml.Matrix4f;
public class BoardShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
uniform mat4 camMatrix;
out vec3 pass_color;
void main(void){
gl_Position = camMatrix * vec4(position, 1.0);
pass_color = color;
}
""";
private static String fragmentShader = """
#version 330
in vec3 pass_color;
out vec4 out_color;
void main(void){
out_color = vec4(pass_color, 1.0);
}
""";
private int location_CamMatrix = 0;
public BoardShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_CamMatrix = GetUniformLocation("camMatrix");
}
public void SetCamMatrix(Matrix4f mat) {
LoadMat4(location_CamMatrix, mat);
}
}

View File

@@ -1,4 +1,4 @@
package chess.view.DDDrender.shader; package chess.view.render3D.shader;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
@@ -56,8 +56,8 @@ public abstract class ShaderProgram {
if (compileSuccesful.get() != 1) { if (compileSuccesful.get() != 1) {
System.out.println("Shader did not compile !"); System.out.println("Shader did not compile !");
System.err.println(GL30.glGetShaderInfoLog(shaderId));
return -1; return -1;
} }
return shaderId; return shaderId;
@@ -68,7 +68,7 @@ public abstract class ShaderProgram {
protected int GetUniformLocation(String uniformName) { protected int GetUniformLocation(String uniformName) {
int location = GL30.glGetUniformLocation(programId, uniformName); int location = GL30.glGetUniformLocation(programId, uniformName);
if (location == -1) { if (location == -1) {
System.out.println("Uniform value \"" + uniformName + "\" not found !"); System.out.println("Uniform value not found !");
} }
return location; return location;
} }

View File

@@ -1,79 +0,0 @@
package chess.view.simplerender;
import java.awt.Image;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
import chess.view.AssetManager;
public class PieceIcon implements PieceVisitor<String> {
private static final String basePath = "pieces2D/";
private static final Map<String, Icon> cache = new HashMap<>();
public Icon getIcon(Piece piece) throws IOException {
if (piece == null)
return null;
String path = basePath + colorToString(piece.getColor()) + "-" + visit(piece) + ".png";
return getIcon(path);
}
private Icon getIcon(String path) throws IOException {
Icon image = cache.get(path);
if (image != null)
return image;
image = new ImageIcon(new ImageIcon(AssetManager.getResource(path).readAllBytes()).getImage()
.getScaledInstance(100, 100, Image.SCALE_SMOOTH));
cache.put(path, image);
return image;
}
private String colorToString(Color color) {
return color == Color.Black ? "black" : "white";
}
@Override
public String visitPiece(Bishop bishop) {
return "bishop";
}
@Override
public String visitPiece(King king) {
return "king";
}
@Override
public String visitPiece(Knight knight) {
return "knight";
}
@Override
public String visitPiece(Pawn pawn) {
return "pawn";
}
@Override
public String visitPiece(Queen queen) {
return "queen";
}
@Override
public String visitPiece(Rook rook) {
return "rook";
}
}

View File

@@ -1,321 +0,0 @@
package chess.view.simplerender;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import chess.controller.Command;
import chess.controller.Command.CommandResult;
import chess.controller.CommandExecutor;
import chess.controller.commands.CastlingCommand;
import chess.controller.commands.GetAllowedCastlingsCommand;
import chess.controller.commands.GetAllowedMovesPieceCommand;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.commands.PromoteCommand.PromoteType;
import chess.controller.commands.UndoCommand;
import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.event.GameListener;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public class Window extends JFrame implements GameListener {
private final CommandExecutor commandExecutor;
private Coordinate lastClick = null;
private final JLabel cells[][];
private final JLabel displayText;
private final JButton castlingButton = new JButton("Roque");
private final JButton bigCastlingButton = new JButton("Grand Roque");
private final JButton undoButton = new JButton("Annuler le coup précédent");
private final boolean showPopups;
public Window(final CommandExecutor commandExecutor, boolean showPopups) {
this.cells = new JLabel[8][8];
this.displayText = new JLabel();
this.commandExecutor = commandExecutor;
this.showPopups = showPopups;
setSize(800, 910);
setVisible(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
commandExecutor.close();
}
});
}
private CommandResult sendCommand(Command command) {
return this.commandExecutor.executeCommand(command);
}
private Color getCellColor(int x, int y) {
return ((x + y) % 2 == 1) ? Color.DARK_GRAY : Color.LIGHT_GRAY;
}
private void buildButtons(JPanel bottom) {
castlingButton.addActionListener((event) -> {
sendCommand(new CastlingCommand(false));
});
bigCastlingButton.addActionListener((event) -> {
sendCommand(new CastlingCommand(true));
});
undoButton.addActionListener((event) -> {
sendCommand(new UndoCommand());
});
bottom.add(castlingButton);
bottom.add(bigCastlingButton);
bottom.add(undoButton);
}
private void buildBoard() {
JPanel content = new JPanel();
JPanel grid = new JPanel(new GridLayout(8, 8));
JPanel bottom = new JPanel();
buildButtons(bottom);
content.add(this.displayText);
content.add(grid);
content.add(bottom);
setContentPane(content);
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
JLabel label = new JLabel();
label.setOpaque(true);
label.setBackground(getCellColor(x, y));
this.cells[x][y] = label;
final int xx = x;
final int yy = y;
label.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
onCellClicked(xx, yy);
}
});
grid.add(label);
}
}
updateBoard();
}
private boolean isCellEmpty(int x, int y) {
return pieceAt(x, y) == null;
}
private Piece pieceAt(int x, int y) {
GetPieceAtCommand command = new GetPieceAtCommand(new Coordinate(x, y));
sendCommand(command);
return command.getPiece();
}
private void updateBoard() {
PieceIcon pieceIcon = new PieceIcon();
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
JLabel cell = this.cells[x][y];
try {
cell.setIcon(pieceIcon.getIcon(pieceAt(x, y)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private boolean previewMoves(int x, int y) {
GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(new Coordinate(x, y));
if (sendCommand(movesCommand) == CommandResult.NotAllowed)
return false;
List<Coordinate> allowedMoves = movesCommand.getDestinations();
if (allowedMoves.isEmpty())
return false;
for (Coordinate destCoord : allowedMoves) {
JLabel cell = this.cells[destCoord.getX()][destCoord.getY()];
Graphics g = cell.getGraphics();
g.setColor(new Color(128, 128, 128, 128));
g.fillOval(25, 25, 50, 50);
}
return true;
}
private void drawInvalid(Move move) {
JLabel from = this.cells[move.getStart().getX()][move.getStart().getY()];
JLabel to = this.cells[move.getFinish().getX()][move.getFinish().getY()];
from.setBackground(Color.RED);
to.setBackground(Color.RED);
}
private void clearMoves() {
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
JLabel cell = this.cells[x][y];
cell.setBackground(getCellColor(x, y));
cell.paint(cell.getGraphics());
}
}
}
private void onCellClicked(int x, int y) {
clearMoves();
if (this.lastClick == null) {
if (isCellEmpty(x, y))
return;
if (!previewMoves(x, y))
return;
this.lastClick = new Coordinate(x, y);
return;
}
if (!this.lastClick.equals(new Coordinate(x, y))) {
Move move = new Move(lastClick, new Coordinate(x, y));
sendCommand(new MoveCommand(move));
}
this.lastClick = null;
}
private void updateButtons() {
GetAllowedCastlingsCommand cmd = new GetAllowedCastlingsCommand();
sendCommand(cmd);
this.castlingButton.setEnabled(
cmd.getCastlingResult() == CastlingResult.Small || cmd.getCastlingResult() == CastlingResult.Both);
this.bigCastlingButton.setEnabled(
cmd.getCastlingResult() == CastlingResult.Big || cmd.getCastlingResult() == CastlingResult.Both);
}
@Override
public void onPlayerTurn(chess.model.Color color, boolean undone) {
this.displayText.setText("Current turn: " + color);
updateButtons();
}
@Override
public void onWin(chess.model.Color color) {
JOptionPane.showMessageDialog(this, "Victory of " + color);
}
@Override
public void onKingInCheck() {
if (!showPopups)
return;
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, "Check!");
});
}
@Override
public void onKingInMat() {
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this, "Checkmate!");
});
}
@Override
public void onPatSituation() {
JOptionPane.showMessageDialog(this, "Pat. It's a draw!");
}
@Override
public void onSurrender(chess.model.Color color) {
JOptionPane.showMessageDialog(this, color + " has surrendered.");
}
@Override
public void onGameEnd() {
JOptionPane.showMessageDialog(this, "End of the game");
this.dispose();
this.commandExecutor.close();
}
@Override
public void onGameStart() {
buildBoard();
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
if (!showPopups)
return;
SwingUtilities.invokeLater(() -> {
String result = null;
Object[] possibilities = new Object[PromoteType.values().length];
int i = 0;
for (PromoteType type : PromoteType.values()) {
possibilities[i] = type.name();
i++;
}
while (result == null || result.isEmpty()) {
result = (String) JOptionPane.showInputDialog(
this,
"Choose the type of piece to upgrade the pawn",
"Promote Dialog",
JOptionPane.PLAIN_MESSAGE,
null,
possibilities,
possibilities[0]);
}
PromoteType choosedType = null;
for (PromoteType type : PromoteType.values()) {
if (type.name().equals(result)) {
choosedType = type;
break;
}
}
if (choosedType != null)
sendCommand(new PromoteCommand(choosedType));
});
}
@Override
public void onBoardUpdate() {
updateBoard();
}
@Override
public void onMove(Move move) {}
@Override
public void onMoveNotAllowed(Move move) {
drawInvalid(move);
}
@Override
public void onDraw() {
JOptionPane.showMessageDialog(this, "Same position was repeated three times. It's a draw!");
}
}

View File

@@ -1,26 +0,0 @@
package common;
import java.util.ArrayList;
import java.util.List;
public class Signal0 {
private final List<Runnable> handlers;
public Signal0() {
this.handlers = new ArrayList<>();
}
public void connect(Runnable handler) {
this.handlers.add(handler);
}
public void disconnect(Runnable handler) {
this.handlers.remove(handler);
}
public void emit() {
for (Runnable handler : this.handlers) {
handler.run();
}
}
}

View File

@@ -1,27 +0,0 @@
package common;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class Signal1<T> {
private final List<Consumer<T>> handlers;
public Signal1() {
this.handlers = new ArrayList<>();
}
public void connect(Consumer<T> handler) {
this.handlers.add(handler);
}
public void disconnect(Consumer<T> handler) {
this.handlers.remove(handler);
}
public void emit(T arg) {
for (Consumer<T> handler : this.handlers) {
handler.accept(arg);
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More