Compare commits
7 Commits
ab309ae48a
...
decorateur
| Author | SHA1 | Date | |
|---|---|---|---|
| fa3c6672e4 | |||
| b920c4fbb3 | |||
|
|
f4b5e10e5b | ||
|
|
747bc62596 | ||
|
|
d60e66fd09 | ||
|
|
c8c6019b15 | ||
|
|
d720e16de0 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# Linux start script should use lf
|
||||
/gradlew text eol=lf
|
||||
|
||||
# These are Windows script files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,5 +5,3 @@
|
||||
build
|
||||
|
||||
app/bin
|
||||
|
||||
.vscode
|
||||
@@ -17,7 +17,7 @@ repositories {
|
||||
}
|
||||
|
||||
def lwjgl_version = "3.3.6"
|
||||
def lwjgl_natives = "natives-linux"
|
||||
def lwjgl_natives = "natives-windows"
|
||||
|
||||
dependencies {
|
||||
// Use JUnit Jupiter for testing.
|
||||
@@ -26,29 +26,16 @@ dependencies {
|
||||
implementation "org.lwjgl:lwjgl:$lwjgl_version"
|
||||
implementation "org.lwjgl:lwjgl-opengl:$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.lwjgl:lwjgl::$lwjgl_natives"
|
||||
implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives"
|
||||
implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives"
|
||||
implementation "org.lwjgl:lwjgl-assimp::$lwjgl_natives"
|
||||
}
|
||||
|
||||
application {
|
||||
// Define the main class for the application.
|
||||
mainClass = "chess.App"
|
||||
applicationName = "3DChess"
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes 'Main-Class': application.mainClass
|
||||
}
|
||||
}
|
||||
|
||||
run {
|
||||
standardInput = System.in
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
/*
|
||||
* This Java source file was generated by the Gradle 'init' task.
|
||||
*/
|
||||
package chess;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
import chess.view.consolerender.Colors;
|
||||
import chess.view.render2D.Window;
|
||||
|
||||
public class App {
|
||||
public String getGreeting() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(Colors.RED + "Credits: Grenier Lilas, Pribylski Simon." + Colors.RESET);
|
||||
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;
|
||||
}
|
||||
Window.main(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
29
app/src/main/java/chess/controller/Controller.java
Normal file
29
app/src/main/java/chess/controller/Controller.java
Normal 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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
17
app/src/main/java/chess/model/AccessibleCellsDecorator.java
Normal file
17
app/src/main/java/chess/model/AccessibleCellsDecorator.java
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
81
app/src/main/java/chess/model/Board.java
Normal file
81
app/src/main/java/chess/model/Board.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
25
app/src/main/java/chess/model/Cell.java
Normal file
25
app/src/main/java/chess/model/Cell.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package chess.model;
|
||||
|
||||
public enum Color {
|
||||
White,
|
||||
Black;
|
||||
|
||||
public static Color getEnemy(Color color) {
|
||||
return color == White ? Black : White;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
22
app/src/main/java/chess/model/Coordinates.java
Normal file
22
app/src/main/java/chess/model/Coordinates.java
Normal 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 + ")";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +1,87 @@
|
||||
package chess.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Observable;
|
||||
|
||||
import chess.controller.PlayerCommand;
|
||||
import chess.controller.commands.MoveCommand;
|
||||
public class Game extends Observable implements Runnable {
|
||||
private final Board board;
|
||||
private Move move;
|
||||
private final ArrayList<Player> players = new ArrayList<>();
|
||||
|
||||
public class Game {
|
||||
private final ChessBoard board;
|
||||
private Color playerTurn;
|
||||
private final Stack<PlayerCommand> movesHistory;
|
||||
private final Map<Integer, Integer> traitsPos;
|
||||
|
||||
private static final int DRAW_REPETITONS = 3;
|
||||
|
||||
public enum GameStatus {
|
||||
Draw, Check, CheckMate, OnGoing, Pat;
|
||||
public Game(int size) {
|
||||
this.board = new Board(size);
|
||||
this.setChanged();
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
public Game() {
|
||||
this.board = new ChessBoard();
|
||||
this.movesHistory = new Stack<>();
|
||||
this.traitsPos = new HashMap<>();
|
||||
this(8);
|
||||
}
|
||||
|
||||
public ChessBoard getBoard() {
|
||||
public int getSize(){
|
||||
return board.getSize();
|
||||
}
|
||||
public Board getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public Color getPlayerTurn() {
|
||||
return playerTurn;
|
||||
public Move getMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
resetPlayerTurn();
|
||||
this.traitsPos.clear();
|
||||
}
|
||||
|
||||
public void resetPlayerTurn() {
|
||||
this.playerTurn = Color.White;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param color the current player turn
|
||||
* @return true if a draw should be declared
|
||||
*/
|
||||
public void saveTraitPiecesPos() {
|
||||
int piecesHash = this.board.hashCode();
|
||||
Integer count = this.traitsPos.get(piecesHash);
|
||||
this.traitsPos.put(piecesHash, count == null ? 1 : count + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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 setMove(Move m){
|
||||
this.move = m;
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void undoTraitPiecesPos() {
|
||||
int piecesHash = this.board.hashCode();
|
||||
Integer count = this.traitsPos.get(piecesHash);
|
||||
if (count != null)
|
||||
this.traitsPos.put(piecesHash, count - 1);
|
||||
public boolean gameDone(){
|
||||
// todo
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<PlayerCommand> getMoves() {
|
||||
return this.movesHistory;
|
||||
public Player getNextPlayer(){
|
||||
return players.getFirst();
|
||||
}
|
||||
|
||||
public void updatePlayersList(){
|
||||
Player player = players.getFirst();
|
||||
players.remove(player);
|
||||
players.add(player);
|
||||
}
|
||||
|
||||
public void playGame() {
|
||||
players.add(new Player(this));
|
||||
players.add(new Player(this));
|
||||
while (!gameDone()){
|
||||
Move m = getNextPlayer().getMove();
|
||||
while (!applyMove(m)){
|
||||
m = getNextPlayer().getMove();
|
||||
}
|
||||
updatePlayersList();
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
playGame();
|
||||
}
|
||||
|
||||
public boolean applyMove(Move m){
|
||||
if (board.getCell(m.getStart()).getPiece() == null){
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
move(m);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void move(Move move) throws Exception{
|
||||
board.movePiece(move);
|
||||
}
|
||||
}
|
||||
|
||||
15
app/src/main/java/chess/model/Model2D.java
Normal file
15
app/src/main/java/chess/model/Model2D.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,26 @@
|
||||
package chess.model;
|
||||
|
||||
public class Move {
|
||||
private final Coordinate start;
|
||||
private final Coordinate finish;
|
||||
private Coordinate deadPieceCoords;
|
||||
private Coordinates start;
|
||||
private Coordinates end;
|
||||
|
||||
public Move(Coordinate start, Coordinate finish) {
|
||||
public Move(Coordinates start, Coordinates end) {
|
||||
this.start = start;
|
||||
this.finish = finish;
|
||||
this.deadPieceCoords = finish;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return this.start.isValid() && this.finish.isValid() && !this.start.equals(this.finish);
|
||||
public Coordinates getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public Coordinate getStart() {
|
||||
public Coordinates getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public Coordinate getFinish() {
|
||||
return finish;
|
||||
public void updateMove(Coordinates start, Coordinates end) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
package chess.model;
|
||||
|
||||
import chess.view.render2D.Pieces;
|
||||
|
||||
public abstract class Piece {
|
||||
public final int color;
|
||||
public AccessibleCellsDecorator decorator;
|
||||
public final Board board;
|
||||
public Cell position;
|
||||
|
||||
private final Color color;
|
||||
private int moved;
|
||||
|
||||
public Piece(Color color) {
|
||||
public Piece(int color, Board board, Cell position) {
|
||||
this.color = color;
|
||||
this.moved = 0;
|
||||
this.board = board;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public void move() {
|
||||
this.moved++;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean hasMoved() {
|
||||
return moved > 0;
|
||||
public void setPosition(Cell cell) {
|
||||
this.position = cell;
|
||||
}
|
||||
|
||||
public void unMove() {
|
||||
this.moved--;
|
||||
}
|
||||
|
||||
public abstract <T> T accept(PieceVisitor<T> visitor);
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object other);
|
||||
public abstract void accept(PieceVisitor pv);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
package chess.model;
|
||||
|
||||
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.model.pieces.*;
|
||||
|
||||
public interface PieceVisitor<T> {
|
||||
|
||||
default T visit(Piece piece) {
|
||||
return piece.accept(this);
|
||||
public interface PieceVisitor{
|
||||
default void visit(Piece p) {
|
||||
p.accept(this);
|
||||
}
|
||||
|
||||
T visitPiece(Bishop bishop);
|
||||
|
||||
T visitPiece(King king);
|
||||
|
||||
T visitPiece(Knight knight);
|
||||
|
||||
T visitPiece(Pawn pawn);
|
||||
|
||||
T visitPiece(Queen queen);
|
||||
|
||||
T visitPiece(Rook rook);
|
||||
void visitPiece(Bishop p);
|
||||
void visitPiece(Knight p);
|
||||
void visitPiece(Pawn p);
|
||||
void visitPiece(Queen p);
|
||||
void visitPiece(King p);
|
||||
void visitPiece(Rook p);
|
||||
|
||||
}
|
||||
|
||||
21
app/src/main/java/chess/model/Player.java
Normal file
21
app/src/main/java/chess/model/Player.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
22
app/src/main/java/chess/model/decorators/LineDecorator.java
Normal file
22
app/src/main/java/chess/model/decorators/LineDecorator.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,17 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Board;
|
||||
import chess.model.Cell;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
|
||||
public class Bishop extends Piece {
|
||||
|
||||
public Bishop(Color color) {
|
||||
super(color);
|
||||
public Bishop(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T accept(PieceVisitor<T> visitor) {
|
||||
return visitor.visitPiece(this);
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof Bishop && ((Bishop) obj).getColor() == this.getColor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Board;
|
||||
import chess.model.Cell;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
|
||||
public class King extends Piece {
|
||||
|
||||
public King(Color color) {
|
||||
super(color);
|
||||
public King(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
}
|
||||
|
||||
@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());
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Board;
|
||||
import chess.model.Cell;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
|
||||
public class Knight extends Piece {
|
||||
|
||||
public Knight(Color color) {
|
||||
super(color);
|
||||
public Knight(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
}
|
||||
|
||||
@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());
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,17 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Board;
|
||||
import chess.model.Cell;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
|
||||
public class Pawn extends Piece {
|
||||
|
||||
public Pawn(Color color) {
|
||||
super(color);
|
||||
public Pawn(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
}
|
||||
|
||||
@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());
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,16 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
import chess.model.*;
|
||||
import chess.model.decorators.DiagonalDecorator;
|
||||
import chess.model.decorators.LineDecorator;
|
||||
|
||||
public class Queen extends Piece {
|
||||
|
||||
public Queen(Color color) {
|
||||
super(color);
|
||||
public Queen(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
}
|
||||
|
||||
@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());
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
package chess.model.pieces;
|
||||
|
||||
import chess.model.Color;
|
||||
import chess.model.Board;
|
||||
import chess.model.Cell;
|
||||
import chess.model.Piece;
|
||||
import chess.model.PieceVisitor;
|
||||
import chess.model.decorators.DiagonalDecorator;
|
||||
import chess.model.decorators.LineDecorator;
|
||||
|
||||
public class Rook extends Piece {
|
||||
|
||||
public Rook(Color color) {
|
||||
super(color);
|
||||
public Rook(final int color, Board board, Cell cell) {
|
||||
super(color, board, cell);
|
||||
this.decorator = new LineDecorator();
|
||||
// todo
|
||||
}
|
||||
|
||||
@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());
|
||||
public void accept(PieceVisitor pv) {
|
||||
pv.visitPiece(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package chess.view.DDDrender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DDDModel {
|
||||
private final List<VertexArray> vaos;
|
||||
|
||||
public DDDModel(List<VertexArray> vaos) {
|
||||
this.vaos = vaos;
|
||||
}
|
||||
|
||||
public List<VertexArray> getVaos() {
|
||||
return vaos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +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 new Vector2f(1.0f - 0.125f - coo.getX() * 0.250f, 1.0f - 0.125f - coo.getY() * 0.250f);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package chess.view.DDDrender;
|
||||
|
||||
import chess.controller.CommandExecutor;
|
||||
import chess.controller.event.GameAdaptator;
|
||||
|
||||
public class DDDView extends GameAdaptator{
|
||||
|
||||
private final CommandExecutor commandExecutor;
|
||||
private final Window window;
|
||||
private final Renderer renderer;
|
||||
|
||||
public DDDView(CommandExecutor commandExecutor) {
|
||||
this.commandExecutor = commandExecutor;
|
||||
this.renderer = new Renderer();
|
||||
this.window = new Window(commandExecutor, this.renderer);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.window.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package chess.view.DDDrender;
|
||||
|
||||
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 java.util.stream.IntStream;
|
||||
|
||||
import org.lwjgl.PointerBuffer;
|
||||
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;
|
||||
|
||||
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++) {
|
||||
int offset = indicies.size();
|
||||
int numIndices = faces.get(i).mNumIndices();
|
||||
IntBuffer faceIndicies = faces.get(i).mIndices();
|
||||
// IntStream.of(faceIndicies.array()).forEach(indicies::add);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package chess.view.DDDrender;
|
||||
|
||||
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;
|
||||
|
||||
public class PieceModel 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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package chess.view.DDDrender;
|
||||
|
||||
public record VertexAttribPointer(int index, int size, int offset) {
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package chess.view.DDDrender.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;
|
||||
uniform vec3 lightPosition;
|
||||
|
||||
flat out vec3 pass_color;
|
||||
out vec3 toLightVector;
|
||||
|
||||
void main(void){
|
||||
gl_Position = camMatrix * vec4(position, 1.0);
|
||||
|
||||
toLightVector = lightPosition - position;
|
||||
|
||||
pass_color = color;
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
private static String fragmentShader = """
|
||||
#version 330
|
||||
|
||||
flat in vec3 pass_color;
|
||||
|
||||
in vec3 toLightVector;
|
||||
|
||||
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.3, 0.03, 0);
|
||||
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
|
||||
|
||||
vec3 unitNormal = vec3(0, 1, 0);
|
||||
vec3 unitLightVector = normalize(toLightVector);
|
||||
|
||||
vec3 lightDirection = -unitLightVector;
|
||||
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
|
||||
|
||||
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
|
||||
|
||||
float brightness = diffuse / attenuationFactor;
|
||||
|
||||
out_color = brightness * vec4(pass_color, 1.0);
|
||||
out_color.w = 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);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package chess.view.DDDrender.shader;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
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 camMatrix;
|
||||
uniform mat4 modelTransform;
|
||||
uniform vec3 lightPosition = vec3(0, 1, 0);
|
||||
|
||||
out vec3 toLightVector;
|
||||
out vec3 surfaceNormal;
|
||||
|
||||
flat out vec3 pass_color;
|
||||
|
||||
void main(void){
|
||||
vec4 worldPos = modelTransform * vec4(position, 1.0);
|
||||
|
||||
toLightVector = lightPosition - worldPos.xyz;
|
||||
surfaceNormal = (modelTransform * vec4(normal, 0.0)).xyz;
|
||||
|
||||
gl_Position = camMatrix * worldPos;
|
||||
pass_color = position;
|
||||
}
|
||||
""";
|
||||
|
||||
private static String fragmentShader = """
|
||||
#version 330
|
||||
|
||||
in vec3 toLightVector;
|
||||
in vec3 surfaceNormal;
|
||||
|
||||
flat in vec3 pass_color;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
void main(void){
|
||||
vec3 unitNormal = normalize(surfaceNormal);
|
||||
vec3 unitLightVector = normalize(toLightVector);
|
||||
|
||||
float diffuse = max(0.5, dot(unitNormal, unitLightVector));
|
||||
|
||||
float brightness = diffuse;
|
||||
|
||||
out_color = vec4(pass_color, 1.0) * brightness;
|
||||
out_color.w = 1.0;
|
||||
|
||||
}
|
||||
""";
|
||||
|
||||
private int location_CamMatrix = 0;
|
||||
private int location_ModelTransform = 0;
|
||||
|
||||
public PieceShader() {
|
||||
|
||||
}
|
||||
|
||||
public void LoadShader() {
|
||||
super.LoadProgram(vertexShader, fragmentShader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void GetAllUniformLocation() {
|
||||
location_CamMatrix = GetUniformLocation("camMatrix");
|
||||
location_ModelTransform = GetUniformLocation("modelTransform");
|
||||
}
|
||||
|
||||
public void SetCamMatrix(Matrix4f mat) {
|
||||
LoadMat4(location_CamMatrix, mat);
|
||||
}
|
||||
|
||||
public void setModelTransform(Matrix4f mat) {
|
||||
LoadMat4(location_ModelTransform, mat);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/chess/view/render2D/PieceFileName.java
Normal file
61
app/src/main/java/chess/view/render2D/PieceFileName.java
Normal 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";
|
||||
}
|
||||
}
|
||||
23
app/src/main/java/chess/view/render2D/Pieces.java
Normal file
23
app/src/main/java/chess/view/render2D/Pieces.java
Normal 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));
|
||||
}
|
||||
}
|
||||
105
app/src/main/java/chess/view/render2D/Window.java
Normal file
105
app/src/main/java/chess/view/render2D/Window.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package chess.view.DDDrender;
|
||||
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 = 1000.0f;
|
||||
|
||||
private float aspectRatio;
|
||||
public static final float zFar = 100.0f;
|
||||
|
||||
private Vector3f pos;
|
||||
|
||||
@@ -16,7 +16,7 @@ public class Camera {
|
||||
private float pitch = 0.0f;
|
||||
|
||||
public Camera() {
|
||||
this.pos = new Vector3f(1.5f, 1.5f, 0);
|
||||
this.pos = new Vector3f(0, 2.0f, 0);
|
||||
setRotation(0.0f, -3.14150f / 2.0f);
|
||||
}
|
||||
|
||||
@@ -115,13 +115,14 @@ public class Camera {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public void setAspectRatio(float aspectRatio) {
|
||||
this.aspectRatio = aspectRatio;
|
||||
}
|
||||
|
||||
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)), aspectRatio, zNear, zFar)
|
||||
.lookAt(pos, new Vector3f(0.0f, 0, 0), new Vector3f(0.0f, 1.0f, 0.0f));
|
||||
.perspective((float) (Math.toRadians(fov)), aspect, zNear, zFar)
|
||||
.lookAt(pos, forward, new Vector3f(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package chess.view.DDDrender;
|
||||
package chess.view.render3D;
|
||||
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
package chess.view.DDDrender;
|
||||
package chess.view.render3D;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
|
||||
import static org.lwjgl.opengl.GL11.glEnable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.*;
|
||||
|
||||
import chess.model.Coordinate;
|
||||
import chess.model.Piece;
|
||||
import chess.view.DDDrender.shader.BoardShader;
|
||||
import chess.view.DDDrender.shader.PieceShader;
|
||||
import chess.view.DDDrender.shader.ShaderProgram;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
import chess.view.render3D.shader.BoardShader;
|
||||
|
||||
public class Renderer {
|
||||
private BoardShader boardShader;
|
||||
private PieceShader pieceShader;
|
||||
private BoardShader shader;
|
||||
private VertexArray vao;
|
||||
private final PieceModel models;
|
||||
|
||||
private static int BOARD_WIDTH = 8;
|
||||
private static int BOARD_HEIGHT = 8;
|
||||
@@ -29,15 +16,11 @@ public class Renderer {
|
||||
private static int SQUARE_VERTEX_COUNT = 4;
|
||||
|
||||
public Renderer() {
|
||||
this.boardShader = new BoardShader();
|
||||
this.pieceShader = new PieceShader();
|
||||
this.models = new PieceModel();
|
||||
this.shader = new BoardShader();
|
||||
}
|
||||
|
||||
public void Init() {
|
||||
boardShader.LoadShader();
|
||||
pieceShader.LoadShader();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
shader.LoadShader();
|
||||
InitBoard();
|
||||
}
|
||||
|
||||
@@ -80,7 +63,7 @@ public class Renderer {
|
||||
for (int i = 0; i < BOARD_WIDTH; i++) {
|
||||
for (int j = 0; j < BOARD_HEIGHT; j++) {
|
||||
Vector3f color;
|
||||
if ((i + j) % 2 == 0) {
|
||||
if ((i + j) % 2 != 0) {
|
||||
color = new Vector3f(1.0f, 1.0f, 1.0f);
|
||||
} else {
|
||||
color = new Vector3f(0.0f, 0.0f, 0.0f);
|
||||
@@ -125,40 +108,14 @@ public class Renderer {
|
||||
this.vao.Unbind();
|
||||
}
|
||||
|
||||
public void RenderPiece(Piece piece, Coordinate pos) {
|
||||
try {
|
||||
DDDModel pieceModel = this.models.getModel(piece);
|
||||
Render(pieceModel, DDDPlacement.coordinates_to_vector(pos));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(Camera cam) {
|
||||
GL30.glClear(GL30.GL_DEPTH_BUFFER_BIT);
|
||||
this.boardShader.Start();
|
||||
this.boardShader.SetCamMatrix(cam.getMatrix());
|
||||
this.pieceShader.Start();
|
||||
this.pieceShader.SetCamMatrix(cam.getMatrix());
|
||||
RenderVao(this.boardShader, vao);
|
||||
this.shader.Start();
|
||||
this.shader.SetCamMatrix(cam.getMatrix());
|
||||
RenderVao(vao);
|
||||
}
|
||||
|
||||
public void Render(DDDModel model, Vector2f position) {
|
||||
Vector3f realPos = new Vector3f(position.x(), 0, position.y());
|
||||
this.pieceShader.Start();
|
||||
this.pieceShader.setModelTransform(new Matrix4f().translate(realPos));
|
||||
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();
|
||||
public void RenderVao(VertexArray vertexArray) {
|
||||
this.shader.Start();
|
||||
vertexArray.Bind();
|
||||
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
|
||||
vertexArray.Unbind();
|
||||
@@ -1,4 +1,4 @@
|
||||
package chess.view.DDDrender;
|
||||
package chess.view.render3D;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package chess.view.DDDrender;
|
||||
package chess.view.render3D;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_FLOAT;
|
||||
|
||||
@@ -41,9 +41,9 @@ public class VertexBuffer {
|
||||
|
||||
public void BindVertexAttribs() {
|
||||
for (VertexAttribPointer vertexAttribPointer : vertexAttribs) {
|
||||
GL30.glEnableVertexAttribArray(vertexAttribPointer.index());
|
||||
GL30.glVertexAttribPointer(vertexAttribPointer.index(), vertexAttribPointer.size(), GL_FLOAT, false,
|
||||
this.dataStride * 4, vertexAttribPointer.offset());
|
||||
GL30.glEnableVertexAttribArray(vertexAttribPointer.index);
|
||||
GL30.glVertexAttribPointer(vertexAttribPointer.index, vertexAttribPointer.size, GL_FLOAT, false,
|
||||
this.dataStride * 4, vertexAttribPointer.offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
package chess.view.DDDrender;
|
||||
package chess.view.render3D;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.*;
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.system.*;
|
||||
|
||||
import chess.controller.CommandExecutor;
|
||||
import chess.controller.commands.GetPieceAtCommand;
|
||||
import chess.model.Coordinate;
|
||||
import chess.model.Piece;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.*;
|
||||
|
||||
import static org.lwjgl.glfw.Callbacks.*;
|
||||
@@ -27,12 +20,10 @@ public class Window {
|
||||
|
||||
private Renderer renderer;
|
||||
private Camera cam;
|
||||
private final CommandExecutor commandExecutor;
|
||||
|
||||
public Window(CommandExecutor commandExecutor, Renderer renderer) {
|
||||
public Window() {
|
||||
this.renderer = new Renderer();
|
||||
this.cam = new Camera();
|
||||
this.commandExecutor = commandExecutor;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
@@ -65,7 +56,7 @@ public class Window {
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
|
||||
|
||||
// Create the window
|
||||
window = glfwCreateWindow(1000, 1000, "3DChess", NULL, NULL);
|
||||
window = glfwCreateWindow(1000, 1000, "Chess4J", NULL, NULL);
|
||||
if (window == NULL)
|
||||
throw new RuntimeException("Failed to create the GLFW window");
|
||||
|
||||
@@ -97,35 +88,8 @@ public class Window {
|
||||
}
|
||||
|
||||
private void render() {
|
||||
final float angle = 0.01f;
|
||||
float x = cam.getPos().x();
|
||||
float y = cam.getPos().z();
|
||||
cam.setPosition(new Vector3f(x * (float) Math.cos(angle) - y * (float) Math.sin(angle), 1.0f,
|
||||
x * (float) Math.sin(angle) + y * (float) Math.cos(angle)));
|
||||
int width[] = new int[1];
|
||||
int height[] = new int[1];
|
||||
glfwGetWindowSize(window, width, height);
|
||||
cam.setAspectRatio((float) width[0] / (float) height[0]);
|
||||
cam.rotate(0.01f, 0.01f);
|
||||
renderer.Render(cam);
|
||||
renderPieces();
|
||||
}
|
||||
|
||||
private Piece pieceAt(Coordinate pos) {
|
||||
GetPieceAtCommand cmd = new GetPieceAtCommand(pos);
|
||||
this.commandExecutor.executeCommand(cmd);
|
||||
return cmd.getPiece();
|
||||
}
|
||||
|
||||
private void renderPieces() {
|
||||
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;
|
||||
this.renderer.RenderPiece(pieceAt(pos), pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package chess.view.DDDrender.shader;
|
||||
package chess.view.render3D.shader;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
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
Reference in New Issue
Block a user