Compare commits
7 Commits
9bc09cf812
...
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
|
# Linux start script should use lf
|
||||||
/gradlew text eol=lf
|
/gradlew text eol=lf
|
||||||
|
|
||||||
# These are Windows script files and should use crlf
|
# These are Windows script files and should use crlf
|
||||||
*.bat text eol=crlf
|
*.bat text eol=crlf
|
||||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
|
||||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,5 +5,3 @@
|
|||||||
build
|
build
|
||||||
|
|
||||||
app/bin
|
app/bin
|
||||||
|
|
||||||
.vscode
|
|
||||||
@@ -17,7 +17,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def lwjgl_version = "3.3.6"
|
def lwjgl_version = "3.3.6"
|
||||||
def lwjgl_natives = "natives-linux"
|
def lwjgl_natives = "natives-windows"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Use JUnit Jupiter for testing.
|
// Use JUnit Jupiter for testing.
|
||||||
@@ -26,29 +26,16 @@ dependencies {
|
|||||||
implementation "org.lwjgl:lwjgl:$lwjgl_version"
|
implementation "org.lwjgl:lwjgl:$lwjgl_version"
|
||||||
implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version"
|
implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version"
|
||||||
implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version"
|
implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version"
|
||||||
implementation "org.lwjgl:lwjgl-assimp:$lwjgl_version"
|
|
||||||
implementation "org.joml:joml:1.10.8"
|
implementation "org.joml:joml:1.10.8"
|
||||||
|
|
||||||
implementation "org.lwjgl:lwjgl::$lwjgl_natives"
|
implementation "org.lwjgl:lwjgl::$lwjgl_natives"
|
||||||
implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives"
|
implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives"
|
||||||
implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives"
|
implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives"
|
||||||
implementation "org.lwjgl:lwjgl-assimp::$lwjgl_natives"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
// Define the main class for the application.
|
// Define the main class for the application.
|
||||||
mainClass = "chess.App"
|
mainClass = "chess.App"
|
||||||
applicationName = "3DChess"
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes 'Main-Class': application.mainClass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run {
|
|
||||||
standardInput = System.in
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|||||||
@@ -1,30 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This Java source file was generated by the Gradle 'init' task.
|
||||||
|
*/
|
||||||
package chess;
|
package chess;
|
||||||
|
|
||||||
import java.util.Scanner;
|
import chess.view.render2D.Window;
|
||||||
|
|
||||||
import chess.view.consolerender.Colors;
|
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
public static void main(String[] args) {
|
public String getGreeting() {
|
||||||
System.out.println(Colors.RED + "Credits: Grenier Lilas, Pribylski Simon." + Colors.RESET);
|
return "Hello World!";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
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;
|
package chess.model;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.Observable;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import chess.controller.PlayerCommand;
|
public class Game extends Observable implements Runnable {
|
||||||
import chess.controller.commands.MoveCommand;
|
private final Board board;
|
||||||
|
private Move move;
|
||||||
|
private final ArrayList<Player> players = new ArrayList<>();
|
||||||
|
|
||||||
public class Game {
|
public Game(int size) {
|
||||||
private final ChessBoard board;
|
this.board = new Board(size);
|
||||||
private Color playerTurn;
|
this.setChanged();
|
||||||
private final Stack<PlayerCommand> movesHistory;
|
this.notifyObservers();
|
||||||
private final Map<Integer, Integer> traitsPos;
|
|
||||||
|
|
||||||
private static final int DRAW_REPETITONS = 3;
|
|
||||||
|
|
||||||
public enum GameStatus {
|
|
||||||
Draw, Check, CheckMate, OnGoing, Pat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Game() {
|
public Game() {
|
||||||
this.board = new ChessBoard();
|
this(8);
|
||||||
this.movesHistory = new Stack<>();
|
|
||||||
this.traitsPos = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChessBoard getBoard() {
|
public int getSize(){
|
||||||
|
return board.getSize();
|
||||||
|
}
|
||||||
|
public Board getBoard() {
|
||||||
return board;
|
return board;
|
||||||
}
|
}
|
||||||
|
public Move getMove() {
|
||||||
public Color getPlayerTurn() {
|
return move;
|
||||||
return playerTurn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void setMove(Move m){
|
||||||
resetPlayerTurn();
|
this.move = m;
|
||||||
this.traitsPos.clear();
|
synchronized (this) {
|
||||||
}
|
this.notify();
|
||||||
|
|
||||||
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 undoTraitPiecesPos() {
|
public boolean gameDone(){
|
||||||
int piecesHash = this.board.hashCode();
|
// todo
|
||||||
Integer count = this.traitsPos.get(piecesHash);
|
return false;
|
||||||
if (count != null)
|
|
||||||
this.traitsPos.put(piecesHash, count - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PlayerCommand> getMoves() {
|
public Player getNextPlayer(){
|
||||||
return this.movesHistory;
|
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;
|
package chess.model;
|
||||||
|
|
||||||
public class Move {
|
public class Move {
|
||||||
private final Coordinate start;
|
private Coordinates start;
|
||||||
private final Coordinate finish;
|
private Coordinates end;
|
||||||
private Coordinate deadPieceCoords;
|
|
||||||
|
|
||||||
public Move(Coordinate start, Coordinate finish) {
|
public Move(Coordinates start, Coordinates end) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.finish = finish;
|
this.end = end;
|
||||||
this.deadPieceCoords = finish;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValid() {
|
public Coordinates getEnd() {
|
||||||
return this.start.isValid() && this.finish.isValid() && !this.start.equals(this.finish);
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coordinate getStart() {
|
public Coordinates getStart() {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coordinate getFinish() {
|
public void updateMove(Coordinates start, Coordinates end) {
|
||||||
return finish;
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int traversedCells() {
|
|
||||||
assert isValid() : "Move is invalid!";
|
|
||||||
|
|
||||||
int diffX = getFinish().getX() - getStart().getX();
|
|
||||||
int diffY = getFinish().getY() - getStart().getY();
|
|
||||||
|
|
||||||
assert Math.abs(diffX) < Coordinate.VALUE_MAX : "Move is too big!";
|
|
||||||
assert Math.abs(diffY) < Coordinate.VALUE_MAX : "Move is too big!";
|
|
||||||
|
|
||||||
if (diffX == 0)
|
|
||||||
return Math.abs(diffY);
|
|
||||||
if (diffY == 0)
|
|
||||||
return Math.abs(diffX);
|
|
||||||
if (Math.abs(diffX) == Math.abs(diffY))
|
|
||||||
return Math.abs(diffX);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coordinate getMiddle() {
|
|
||||||
return Coordinate.fromIndex((getStart().toIndex() + getFinish().toIndex()) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeadPieceCoords(Coordinate deadCoords) {
|
|
||||||
this.deadPieceCoords = deadCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coordinate getDeadPieceCoords() {
|
|
||||||
return deadPieceCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj instanceof Move other)
|
|
||||||
return this.start.equals(other.start) && this.finish.equals(other.finish);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,27 @@
|
|||||||
package chess.model;
|
package chess.model;
|
||||||
|
|
||||||
|
import chess.view.render2D.Pieces;
|
||||||
|
|
||||||
public abstract class Piece {
|
public abstract class Piece {
|
||||||
|
public final int color;
|
||||||
|
public AccessibleCellsDecorator decorator;
|
||||||
|
public final Board board;
|
||||||
|
public Cell position;
|
||||||
|
|
||||||
private final Color color;
|
public Piece(int color, Board board, Cell position) {
|
||||||
private int moved;
|
|
||||||
|
|
||||||
public Piece(Color color) {
|
|
||||||
this.color = color;
|
this.color = color;
|
||||||
this.moved = 0;
|
this.board = board;
|
||||||
|
this.position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move() {
|
public int getColor() {
|
||||||
this.moved++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getColor() {
|
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMoved() {
|
public void setPosition(Cell cell) {
|
||||||
return moved > 0;
|
this.position = cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unMove() {
|
public abstract void accept(PieceVisitor pv);
|
||||||
this.moved--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract <T> T accept(PieceVisitor<T> visitor);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract boolean equals(Object other);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,17 @@
|
|||||||
package chess.model;
|
package chess.model;
|
||||||
|
|
||||||
import chess.model.pieces.Bishop;
|
import chess.model.pieces.*;
|
||||||
import chess.model.pieces.King;
|
|
||||||
import chess.model.pieces.Knight;
|
|
||||||
import chess.model.pieces.Pawn;
|
|
||||||
import chess.model.pieces.Queen;
|
|
||||||
import chess.model.pieces.Rook;
|
|
||||||
|
|
||||||
public interface PieceVisitor<T> {
|
public interface PieceVisitor{
|
||||||
|
default void visit(Piece p) {
|
||||||
default T visit(Piece piece) {
|
p.accept(this);
|
||||||
return piece.accept(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
T visitPiece(Bishop bishop);
|
void visitPiece(Bishop p);
|
||||||
|
void visitPiece(Knight p);
|
||||||
T visitPiece(King king);
|
void visitPiece(Pawn p);
|
||||||
|
void visitPiece(Queen p);
|
||||||
T visitPiece(Knight knight);
|
void visitPiece(King p);
|
||||||
|
void visitPiece(Rook p);
|
||||||
T visitPiece(Pawn pawn);
|
|
||||||
|
|
||||||
T visitPiece(Queen queen);
|
|
||||||
|
|
||||||
T visitPiece(Rook rook);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.Board;
|
||||||
|
import chess.model.Cell;
|
||||||
import chess.model.Piece;
|
import chess.model.Piece;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.PieceVisitor;
|
||||||
|
|
||||||
public class Bishop extends Piece {
|
public class Bishop extends Piece {
|
||||||
|
public Bishop(final int color, Board board, Cell cell) {
|
||||||
public Bishop(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
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;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.Board;
|
||||||
|
import chess.model.Cell;
|
||||||
import chess.model.Piece;
|
import chess.model.Piece;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.PieceVisitor;
|
||||||
|
|
||||||
public class King extends Piece {
|
public class King extends Piece {
|
||||||
|
public King(final int color, Board board, Cell cell) {
|
||||||
public King(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
pv.visitPiece(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
return (obj instanceof King && ((King) obj).getColor() == this.getColor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
package chess.model.pieces;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.Board;
|
||||||
|
import chess.model.Cell;
|
||||||
import chess.model.Piece;
|
import chess.model.Piece;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.PieceVisitor;
|
||||||
|
|
||||||
public class Knight extends Piece {
|
public class Knight extends Piece {
|
||||||
|
public Knight(final int color, Board board, Cell cell) {
|
||||||
public Knight(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
pv.visitPiece(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
return (obj instanceof Knight && ((Knight) obj).getColor() == this.getColor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,17 @@
|
|||||||
package chess.model.pieces;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.Board;
|
||||||
|
import chess.model.Cell;
|
||||||
import chess.model.Piece;
|
import chess.model.Piece;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.PieceVisitor;
|
||||||
|
|
||||||
public class Pawn extends Piece {
|
public class Pawn extends Piece {
|
||||||
|
public Pawn(final int color, Board board, Cell cell) {
|
||||||
public Pawn(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
pv.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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,16 @@
|
|||||||
package chess.model.pieces;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.*;
|
||||||
import chess.model.Piece;
|
import chess.model.decorators.DiagonalDecorator;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.decorators.LineDecorator;
|
||||||
|
|
||||||
public class Queen extends Piece {
|
public class Queen extends Piece {
|
||||||
|
public Queen(final int color, Board board, Cell cell) {
|
||||||
public Queen(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
pv.visitPiece(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
return (obj instanceof Queen && ((Queen) obj).getColor() == this.getColor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,21 @@
|
|||||||
package chess.model.pieces;
|
package chess.model.pieces;
|
||||||
|
|
||||||
import chess.model.Color;
|
import chess.model.Board;
|
||||||
|
import chess.model.Cell;
|
||||||
import chess.model.Piece;
|
import chess.model.Piece;
|
||||||
import chess.model.PieceVisitor;
|
import chess.model.PieceVisitor;
|
||||||
|
import chess.model.decorators.DiagonalDecorator;
|
||||||
|
import chess.model.decorators.LineDecorator;
|
||||||
|
|
||||||
public class Rook extends Piece {
|
public class Rook extends Piece {
|
||||||
|
public Rook(final int color, Board board, Cell cell) {
|
||||||
public Rook(Color color) {
|
super(color, board, cell);
|
||||||
super(color);
|
this.decorator = new LineDecorator();
|
||||||
|
// todo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(PieceVisitor<T> visitor) {
|
public void accept(PieceVisitor pv) {
|
||||||
return visitor.visitPiece(this);
|
pv.visitPiece(this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
return (other instanceof Rook && ((Rook) other).getColor() == this.getColor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,83 +0,0 @@
|
|||||||
package chess.view.DDDrender;
|
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
public class Camera {
|
|
||||||
public static final float fov = 70.0f;
|
|
||||||
public static final float zNear = 0.01f;
|
|
||||||
public static final float zFar = 1000.0f;
|
|
||||||
|
|
||||||
private static final Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
|
|
||||||
private static final Vector3f center = new Vector3f(0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
private final float distance = 1.5f;
|
|
||||||
private final float camHeight = 1.5f;
|
|
||||||
|
|
||||||
|
|
||||||
private float aspectRatio;
|
|
||||||
private float angle;
|
|
||||||
|
|
||||||
private Vector3f pos;
|
|
||||||
|
|
||||||
public Camera() {
|
|
||||||
this.pos = new Vector3f(0.0f, camHeight, 0.0f);
|
|
||||||
this.angle = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void move(float x, float y) {
|
|
||||||
this.pos.x += x;
|
|
||||||
this.pos.y += y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRotateAngle(float angle) {
|
|
||||||
this.angle = angle;
|
|
||||||
updatePostion();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePostion() {
|
|
||||||
final float finalX = (float) Math.sin(angle);
|
|
||||||
final float finalZ = (float) -Math.cos(angle);
|
|
||||||
this.pos.set(distance * finalX, this.pos.get(1), distance * finalZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getRotateAngle() {
|
|
||||||
return angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f getPos() {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFov() {
|
|
||||||
return fov;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(float x) {
|
|
||||||
this.pos.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(float y) {
|
|
||||||
this.pos.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setZ(float z) {
|
|
||||||
this.pos.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(Vector3f pos) {
|
|
||||||
this.pos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAspectRatio(float aspectRatio) {
|
|
||||||
this.aspectRatio = aspectRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matrix4f getPerspectiveMatrix() {
|
|
||||||
return new Matrix4f().perspective((float) (Math.toRadians(fov)), aspectRatio, zNear, zFar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matrix4f getViewMatrix() {
|
|
||||||
return new Matrix4f().lookAt(pos, center, up);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package chess.view.DDDrender;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import chess.view.DDDrender.opengl.VertexArray;
|
|
||||||
|
|
||||||
public class DDDModel {
|
|
||||||
private final List<VertexArray> vaos;
|
|
||||||
|
|
||||||
public DDDModel(List<VertexArray> vaos) {
|
|
||||||
this.vaos = vaos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<VertexArray> getVaos() {
|
|
||||||
return vaos;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package chess.view.DDDrender;
|
|
||||||
|
|
||||||
import org.joml.Vector2f;
|
|
||||||
|
|
||||||
import chess.model.Coordinate;
|
|
||||||
|
|
||||||
class DDDPlacement {
|
|
||||||
static public Vector2f coordinates_to_vector(Coordinate coo) {
|
|
||||||
return coordinates_to_vector(coo.getX(), coo.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
static public Vector2f coordinates_to_vector(float x, float y) {
|
|
||||||
return new Vector2f(1.0f - 0.125f - x * 0.250f, 1.0f - 0.125f - y * 0.250f);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package chess.view.DDDrender;
|
|
||||||
|
|
||||||
import org.joml.Vector2f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import chess.controller.CommandExecutor;
|
|
||||||
import chess.controller.commands.GetPieceAtCommand;
|
|
||||||
import chess.controller.event.GameAdaptator;
|
|
||||||
import chess.model.Color;
|
|
||||||
import chess.model.Coordinate;
|
|
||||||
import chess.model.Piece;
|
|
||||||
import chess.view.DDDrender.world.PieceEntity;
|
|
||||||
import chess.view.DDDrender.world.World;
|
|
||||||
|
|
||||||
public class DDDView extends GameAdaptator {
|
|
||||||
|
|
||||||
private static final Vector3f BLACK = new Vector3f(0.3f, 0.3f, 0.3f);
|
|
||||||
private static final Vector3f WHITE = new Vector3f(1.0f, 1.0f, 1.0f);
|
|
||||||
|
|
||||||
private final CommandExecutor commandExecutor;
|
|
||||||
private final Window window;
|
|
||||||
private final Renderer renderer;
|
|
||||||
private final World world;
|
|
||||||
|
|
||||||
public DDDView(CommandExecutor commandExecutor) {
|
|
||||||
this.commandExecutor = commandExecutor;
|
|
||||||
this.renderer = new Renderer();
|
|
||||||
this.world = new World(new Camera());
|
|
||||||
this.window = new Window(this.renderer, this.world);
|
|
||||||
this.window.OnCellClick.connect(this::onCellClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invoked when a cell is clicked
|
|
||||||
private void onCellClick(Coordinate coordinate) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Piece pieceAt(Coordinate pos) {
|
|
||||||
GetPieceAtCommand cmd = new GetPieceAtCommand(pos);
|
|
||||||
this.commandExecutor.executeCommand(cmd);
|
|
||||||
return cmd.getPiece();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGameStart() {
|
|
||||||
this.window.scheduleTask(() -> {
|
|
||||||
initBoard();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initBoard() {
|
|
||||||
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
|
|
||||||
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
|
|
||||||
Coordinate pos = new Coordinate(i, j);
|
|
||||||
Piece piece = pieceAt(pos);
|
|
||||||
if (piece == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2f pieceBoardPos = DDDPlacement.coordinates_to_vector(pos);
|
|
||||||
Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y());
|
|
||||||
|
|
||||||
this.world.addEntity(new PieceEntity(piece, pieceWorldPos,
|
|
||||||
piece.getColor() == Color.White ? WHITE : BLACK,
|
|
||||||
piece.getColor() == Color.White ? 0.0f : (float) Math.PI));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
this.window.addRegularTask((delta) -> {
|
|
||||||
final float angle = 1f;
|
|
||||||
final Camera cam = this.world.getCamera();
|
|
||||||
cam.setRotateAngle(cam.getRotateAngle() + angle * delta);
|
|
||||||
});
|
|
||||||
this.window.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package chess.view.DDDrender;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
|
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
|
|
||||||
import chess.view.DDDrender.loader.BoardModelLoader;
|
|
||||||
import chess.view.DDDrender.opengl.VertexArray;
|
|
||||||
import chess.view.DDDrender.shader.BoardShader;
|
|
||||||
import chess.view.DDDrender.shader.PieceShader;
|
|
||||||
import chess.view.DDDrender.shader.ShaderProgram;
|
|
||||||
|
|
||||||
public class Renderer {
|
|
||||||
private BoardShader boardShader;
|
|
||||||
private PieceShader pieceShader;
|
|
||||||
private VertexArray boardVao;
|
|
||||||
|
|
||||||
public Renderer() {
|
|
||||||
this.boardShader = new BoardShader();
|
|
||||||
this.pieceShader = new PieceShader();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Init() {
|
|
||||||
boardShader.LoadShader();
|
|
||||||
pieceShader.LoadShader();
|
|
||||||
this.boardVao = BoardModelLoader.GetBoardModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(Camera cam) {
|
|
||||||
this.boardShader.Start();
|
|
||||||
this.boardShader.SetCamMatrix(cam);
|
|
||||||
this.pieceShader.Start();
|
|
||||||
this.pieceShader.SetCamMatrix(cam);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RenderBoard() {
|
|
||||||
RenderVao(this.boardShader, this.boardVao);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render(DDDModel model, Vector3f color, Vector3f position, float rotation) {
|
|
||||||
this.pieceShader.Start();
|
|
||||||
this.pieceShader.setModelColor(color);
|
|
||||||
this.pieceShader.setModelTransform(new Matrix4f().translate(position).rotate(rotation, new Vector3f(0, 1, 0)));
|
|
||||||
Render(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render(DDDModel model) {
|
|
||||||
for (int i = 0; i < model.getVaos().size(); i++) {
|
|
||||||
VertexArray vao = model.getVaos().get(i);
|
|
||||||
RenderVao(this.pieceShader, vao);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RenderVao(ShaderProgram shader, VertexArray vertexArray) {
|
|
||||||
shader.Start();
|
|
||||||
vertexArray.Bind();
|
|
||||||
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
|
|
||||||
vertexArray.Unbind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package chess.view.DDDrender.loader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.lwjgl.assimp.AIFace.Buffer;
|
|
||||||
import org.lwjgl.assimp.AIMesh;
|
|
||||||
import org.lwjgl.assimp.AINode;
|
|
||||||
import org.lwjgl.assimp.AIScene;
|
|
||||||
import org.lwjgl.assimp.AIVector3D;
|
|
||||||
import org.lwjgl.assimp.Assimp;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import chess.view.AssetManager;
|
|
||||||
import chess.view.DDDrender.DDDModel;
|
|
||||||
import chess.view.DDDrender.opengl.ElementBuffer;
|
|
||||||
import chess.view.DDDrender.opengl.VertexArray;
|
|
||||||
import chess.view.DDDrender.opengl.VertexBuffer;
|
|
||||||
|
|
||||||
public class ModelLoader {
|
|
||||||
|
|
||||||
private static final int VERTEX_SIZE = 3;
|
|
||||||
private static final int UV_SIZE = 2;
|
|
||||||
private static final int VERTEX_POSITION_INDEX = 0;
|
|
||||||
private static final int VERTEX_UV_INDEX = 1;
|
|
||||||
private static final int VERTEX_NORMAL_INDEX = 2;
|
|
||||||
|
|
||||||
private static float[] toFloatArray(List<Float> list) {
|
|
||||||
float[] result = new float[list.size()];
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
result[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] toIntArray(List<Integer> list) {
|
|
||||||
int[] result = new int[list.size()];
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
result[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static VertexArray processMesh(AIMesh mesh, AIScene scene) {
|
|
||||||
List<Float> positions = new ArrayList<>();
|
|
||||||
List<Float> textureCoords = new ArrayList<>();
|
|
||||||
List<Float> normals = new ArrayList<>();
|
|
||||||
|
|
||||||
List<Integer> indicies = new ArrayList<>();
|
|
||||||
|
|
||||||
Buffer faces = mesh.mFaces();
|
|
||||||
int faceNumber = mesh.mNumFaces();
|
|
||||||
|
|
||||||
for (int i = 0; i < faceNumber; i++) {
|
|
||||||
IntBuffer faceIndicies = faces.get(i).mIndices();
|
|
||||||
for (int j = 0; j < faceIndicies.capacity(); j++) {
|
|
||||||
indicies.add(faceIndicies.get(j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int vertNumber = mesh.mNumVertices();
|
|
||||||
org.lwjgl.assimp.AIVector3D.Buffer vertecies = mesh.mVertices();
|
|
||||||
for (int i = 0; i < vertNumber; i++) {
|
|
||||||
AIVector3D vertex = vertecies.get(i);
|
|
||||||
positions.add(vertex.x());
|
|
||||||
positions.add(vertex.y());
|
|
||||||
positions.add(vertex.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
org.lwjgl.assimp.AIVector3D.Buffer vertexNormals = mesh.mNormals();
|
|
||||||
for (int i = 0; i < vertNumber; i++) {
|
|
||||||
AIVector3D normal = vertexNormals.get(i);
|
|
||||||
normals.add(normal.x());
|
|
||||||
normals.add(normal.y());
|
|
||||||
normals.add(normal.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
// PointerBuffer vertexTexture = mesh.mTextureCoords();
|
|
||||||
// for (int i = 0; i < vertNumber; i++) {
|
|
||||||
// PointerBuffer buff = mesh.mTextureCoords();
|
|
||||||
// textureCoords.add(buff.get(i).x());
|
|
||||||
// textureCoords.add(buff.get(i).y());
|
|
||||||
// }
|
|
||||||
|
|
||||||
VertexBuffer positionVBO = new VertexBuffer(toFloatArray(positions), VERTEX_SIZE);
|
|
||||||
positionVBO.AddVertexAttribPointer(VERTEX_POSITION_INDEX, VERTEX_SIZE, 0);
|
|
||||||
VertexBuffer textureVBO = new VertexBuffer(toFloatArray(textureCoords), UV_SIZE);
|
|
||||||
textureVBO.AddVertexAttribPointer(VERTEX_UV_INDEX, UV_SIZE, 0);
|
|
||||||
VertexBuffer normalVBO = new VertexBuffer(toFloatArray(normals), VERTEX_SIZE);
|
|
||||||
normalVBO.AddVertexAttribPointer(VERTEX_NORMAL_INDEX, VERTEX_SIZE, 0);
|
|
||||||
|
|
||||||
VertexArray vao = new VertexArray(new ElementBuffer(toIntArray(indicies)));
|
|
||||||
vao.Bind();
|
|
||||||
vao.BindVertexBuffer(positionVBO);
|
|
||||||
vao.BindVertexBuffer(textureVBO);
|
|
||||||
vao.BindVertexBuffer(normalVBO);
|
|
||||||
vao.Unbind();
|
|
||||||
return vao;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void processNode(AINode node, AIScene scene, List<VertexArray> meshes) {
|
|
||||||
for (int i = 0; i < node.mNumChildren(); i++) {
|
|
||||||
AINode child = AINode.create(node.mChildren().get(i));
|
|
||||||
processNode(child, scene, meshes);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < node.mNumMeshes(); i++) {
|
|
||||||
AIMesh mesh = AIMesh.create(scene.mMeshes().get(node.mMeshes().get(i)));
|
|
||||||
meshes.add(processMesh(mesh, scene));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DDDModel loadModel(String filename) throws IOException {
|
|
||||||
InputStream input = AssetManager.getResource(filename);
|
|
||||||
byte[] buffer = input.readAllBytes();
|
|
||||||
ByteBuffer data = MemoryUtil.memCalloc(buffer.length);
|
|
||||||
data.put(buffer);
|
|
||||||
data.flip();
|
|
||||||
|
|
||||||
AIScene scene = Assimp.aiImportFileFromMemory(
|
|
||||||
data,
|
|
||||||
Assimp.aiProcess_Triangulate | Assimp.aiProcess_PreTransformVertices | Assimp.aiProcess_GlobalScale
|
|
||||||
| Assimp.aiProcess_ValidateDataStructure,
|
|
||||||
"");
|
|
||||||
|
|
||||||
if (scene == null)
|
|
||||||
System.err.println(Assimp.aiGetErrorString());
|
|
||||||
|
|
||||||
List<VertexArray> vertecies = new ArrayList<>();
|
|
||||||
|
|
||||||
processNode(scene.mRootNode(), scene, vertecies);
|
|
||||||
|
|
||||||
MemoryUtil.memFree(data);
|
|
||||||
|
|
||||||
return new DDDModel(vertecies);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package chess.view.DDDrender.loader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import chess.model.Color;
|
|
||||||
import chess.model.Piece;
|
|
||||||
import chess.model.PieceVisitor;
|
|
||||||
import chess.model.pieces.Bishop;
|
|
||||||
import chess.model.pieces.King;
|
|
||||||
import chess.model.pieces.Knight;
|
|
||||||
import chess.model.pieces.Pawn;
|
|
||||||
import chess.model.pieces.Queen;
|
|
||||||
import chess.model.pieces.Rook;
|
|
||||||
import chess.view.DDDrender.DDDModel;
|
|
||||||
|
|
||||||
public class Piece3DModel implements PieceVisitor<String> {
|
|
||||||
|
|
||||||
private static final String basePath = "3d/";
|
|
||||||
private static final Map<String, DDDModel> cache = new HashMap<>();
|
|
||||||
|
|
||||||
public DDDModel getModel(Piece piece) throws IOException {
|
|
||||||
if (piece == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
String path = basePath + colorToString(piece.getColor()) + "-" + visit(piece) + ".fbx";
|
|
||||||
return getModel(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DDDModel getModel(String path) throws IOException {
|
|
||||||
DDDModel model = cache.get(path);
|
|
||||||
if (model != null)
|
|
||||||
return model;
|
|
||||||
|
|
||||||
model = ModelLoader.loadModel(path);
|
|
||||||
cache.put(path, model);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String colorToString(Color color) {
|
|
||||||
return color == Color.Black ? "black" : "white";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(Bishop bishop) {
|
|
||||||
return "bishop";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(King king) {
|
|
||||||
return "king";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(Knight knight) {
|
|
||||||
return "knight";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(Pawn pawn) {
|
|
||||||
return "pawn";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(Queen queen) {
|
|
||||||
return "queen";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String visitPiece(Rook rook) {
|
|
||||||
return "rook";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package chess.view.DDDrender.opengl;
|
|
||||||
|
|
||||||
public record VertexAttribPointer(int index, int size, int offset) {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package chess.view.DDDrender.shader;
|
|
||||||
|
|
||||||
import chess.view.DDDrender.Camera;
|
|
||||||
|
|
||||||
public class BoardShader extends ShaderProgram {
|
|
||||||
|
|
||||||
private static String vertexShader = """
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 position;
|
|
||||||
layout(location = 1) in vec3 color;
|
|
||||||
|
|
||||||
uniform mat4 viewMatrix;
|
|
||||||
uniform mat4 projectionMatrix;
|
|
||||||
uniform vec3 lightPosition = vec3(0, 10, 0);
|
|
||||||
|
|
||||||
flat out vec3 pass_color;
|
|
||||||
|
|
||||||
out vec3 toLightVector;
|
|
||||||
out vec3 toCameraVector;
|
|
||||||
out vec3 surfaceNormal;
|
|
||||||
|
|
||||||
void main(void){
|
|
||||||
const vec4 normal = vec4(0.0, 1.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
|
|
||||||
|
|
||||||
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
|
||||||
|
|
||||||
toLightVector = lightPosition - position;
|
|
||||||
|
|
||||||
toCameraVector = camPos - position;
|
|
||||||
surfaceNormal = (normal).xyz;
|
|
||||||
|
|
||||||
pass_color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
""";
|
|
||||||
|
|
||||||
private static String fragmentShader = """
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
flat in vec3 pass_color;
|
|
||||||
|
|
||||||
in vec3 toLightVector;
|
|
||||||
in vec3 toCameraVector;
|
|
||||||
in vec3 surfaceNormal;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
|
|
||||||
void main(void){
|
|
||||||
const float shineDamper = 10.0;
|
|
||||||
const float reflectivity = 1.0;
|
|
||||||
|
|
||||||
float lightDistance = length(toLightVector);
|
|
||||||
|
|
||||||
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
|
|
||||||
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
|
|
||||||
|
|
||||||
vec3 unitNormal = normalize(surfaceNormal);
|
|
||||||
vec3 unitLightVector = normalize(toLightVector);
|
|
||||||
vec3 unitCamVector = normalize(toCameraVector);
|
|
||||||
|
|
||||||
vec3 lightDirection = -unitLightVector;
|
|
||||||
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
|
|
||||||
|
|
||||||
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
|
|
||||||
|
|
||||||
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
|
|
||||||
float specular = pow(specularFactor, shineDamper) * reflectivity;
|
|
||||||
|
|
||||||
float brightness = (diffuse + specular) / attenuationFactor;
|
|
||||||
|
|
||||||
out_color = brightness * vec4(pass_color, 1.0);
|
|
||||||
out_color.w = 1.0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
""";
|
|
||||||
|
|
||||||
private int location_ProjectionMatrix = 0;
|
|
||||||
private int location_ViewMatrix = 0;
|
|
||||||
|
|
||||||
public BoardShader() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadShader() {
|
|
||||||
super.LoadProgram(vertexShader, fragmentShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void GetAllUniformLocation() {
|
|
||||||
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
|
|
||||||
location_ViewMatrix = GetUniformLocation("viewMatrix");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetCamMatrix(Camera camera) {
|
|
||||||
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
|
|
||||||
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
package chess.view.DDDrender.shader;
|
|
||||||
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import chess.view.DDDrender.Camera;
|
|
||||||
|
|
||||||
public class PieceShader extends ShaderProgram {
|
|
||||||
|
|
||||||
private static String vertexShader = """
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 position;
|
|
||||||
layout(location = 1) in vec2 uv;
|
|
||||||
layout(location = 2) in vec3 normal;
|
|
||||||
|
|
||||||
uniform mat4 projectionMatrix;
|
|
||||||
uniform mat4 viewMatrix;
|
|
||||||
uniform mat4 modelTransform;
|
|
||||||
uniform vec3 lightPosition = vec3(0, 10, 0);
|
|
||||||
|
|
||||||
out vec3 toLightVector;
|
|
||||||
out vec3 toCameraVector;
|
|
||||||
out vec3 surfaceNormal;
|
|
||||||
|
|
||||||
void main(void){
|
|
||||||
|
|
||||||
vec4 modelPos = modelTransform * vec4(position, 1.0);
|
|
||||||
vec4 globalNormal = modelTransform * vec4(normal, 1.0);
|
|
||||||
|
|
||||||
gl_Position = projectionMatrix * viewMatrix * modelPos;
|
|
||||||
|
|
||||||
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
|
|
||||||
|
|
||||||
toLightVector = lightPosition - modelPos.xyz;
|
|
||||||
|
|
||||||
toCameraVector = camPos - position;
|
|
||||||
surfaceNormal = normalize(globalNormal.xyz);
|
|
||||||
}
|
|
||||||
|
|
||||||
""";
|
|
||||||
|
|
||||||
private static String fragmentShader = """
|
|
||||||
#version 330
|
|
||||||
|
|
||||||
in vec3 toLightVector;
|
|
||||||
in vec3 toCameraVector;
|
|
||||||
in vec3 surfaceNormal;
|
|
||||||
|
|
||||||
uniform vec3 modelColor;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
|
|
||||||
void main(void){
|
|
||||||
const float shineDamper = 10.0;
|
|
||||||
const float reflectivity = 1.0;
|
|
||||||
|
|
||||||
float lightDistance = length(toLightVector);
|
|
||||||
|
|
||||||
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
|
|
||||||
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
|
|
||||||
|
|
||||||
vec3 unitNormal = normalize(surfaceNormal);
|
|
||||||
vec3 unitLightVector = normalize(toLightVector);
|
|
||||||
vec3 unitCamVector = normalize(toCameraVector);
|
|
||||||
|
|
||||||
vec3 lightDirection = -unitLightVector;
|
|
||||||
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
|
|
||||||
|
|
||||||
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
|
|
||||||
|
|
||||||
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
|
|
||||||
float specular = pow(specularFactor, shineDamper) * reflectivity;
|
|
||||||
|
|
||||||
float brightness = (diffuse + specular) / attenuationFactor;
|
|
||||||
|
|
||||||
out_color = brightness * vec4(modelColor, 1.0);
|
|
||||||
out_color.w = 1.0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
""";
|
|
||||||
|
|
||||||
private int location_ProjectionMatrix = 0;
|
|
||||||
private int location_ViewMatrix = 0;
|
|
||||||
private int location_ModelTransform = 0;
|
|
||||||
private int location_ModelColor = 0;
|
|
||||||
|
|
||||||
public PieceShader() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadShader() {
|
|
||||||
super.LoadProgram(vertexShader, fragmentShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void GetAllUniformLocation() {
|
|
||||||
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
|
|
||||||
location_ViewMatrix = GetUniformLocation("viewMatrix");
|
|
||||||
location_ModelTransform = GetUniformLocation("modelTransform");
|
|
||||||
location_ModelColor = GetUniformLocation("modelColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetCamMatrix(Camera camera) {
|
|
||||||
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
|
|
||||||
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModelTransform(Matrix4f mat) {
|
|
||||||
LoadMat4(location_ModelTransform, mat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModelColor(Vector3f color) {
|
|
||||||
LoadVector(location_ModelColor, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package chess.view.DDDrender.world;
|
|
||||||
|
|
||||||
import chess.view.DDDrender.Renderer;
|
|
||||||
|
|
||||||
public abstract class Entity {
|
|
||||||
|
|
||||||
public abstract void render(Renderer renderer);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package chess.view.DDDrender.world;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import chess.model.Piece;
|
|
||||||
import chess.view.DDDrender.DDDModel;
|
|
||||||
import chess.view.DDDrender.Renderer;
|
|
||||||
import chess.view.DDDrender.loader.Piece3DModel;
|
|
||||||
|
|
||||||
public class PieceEntity extends Entity {
|
|
||||||
|
|
||||||
private static final Piece3DModel modelLoader = new Piece3DModel();
|
|
||||||
|
|
||||||
private final Piece piece;
|
|
||||||
private Vector3f position;
|
|
||||||
private Vector3f color;
|
|
||||||
private float rotation;
|
|
||||||
private DDDModel model;
|
|
||||||
|
|
||||||
public PieceEntity(Piece piece, Vector3f position, Vector3f color, float rotation) {
|
|
||||||
this.piece = piece;
|
|
||||||
this.position = position;
|
|
||||||
this.color = color;
|
|
||||||
this.rotation = rotation;
|
|
||||||
try {
|
|
||||||
this.model = modelLoader.getModel(piece);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Piece getPiece() {
|
|
||||||
return piece;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(Renderer renderer) {
|
|
||||||
renderer.Render(model, color, position, rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(Vector3f position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColor(Vector3f color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRotation(float rotation) {
|
|
||||||
this.rotation = rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package chess.view.DDDrender.world;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import chess.model.Piece;
|
|
||||||
import chess.view.DDDrender.Camera;
|
|
||||||
|
|
||||||
public class World {
|
|
||||||
private final Camera camera;
|
|
||||||
private final List<Entity> entites;
|
|
||||||
|
|
||||||
public World(Camera camera) {
|
|
||||||
this.camera = camera;
|
|
||||||
this.entites = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PieceEntity getEntity(Piece piece) {
|
|
||||||
for (Entity entity : entites) {
|
|
||||||
if (entity instanceof PieceEntity p) {
|
|
||||||
if (p.getPiece() == piece)
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addEntity(Entity entity) {
|
|
||||||
this.entites.add(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Camera getCamera() {
|
|
||||||
return camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Entity> getEntites() {
|
|
||||||
return entites;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
128
app/src/main/java/chess/view/render3D/Camera.java
Normal file
128
app/src/main/java/chess/view/render3D/Camera.java
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package chess.view.render3D;
|
||||||
|
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class Camera {
|
||||||
|
public static final float fov = 70.0f;
|
||||||
|
// should be changed to match screen
|
||||||
|
public static final float aspect = 1.0f;
|
||||||
|
public static final float zNear = 0.01f;
|
||||||
|
public static final float zFar = 100.0f;
|
||||||
|
|
||||||
|
private Vector3f pos;
|
||||||
|
|
||||||
|
private float yaw = 0.0f;
|
||||||
|
private float pitch = 0.0f;
|
||||||
|
|
||||||
|
public Camera() {
|
||||||
|
this.pos = new Vector3f(0, 2.0f, 0);
|
||||||
|
setRotation(0.0f, -3.14150f / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(float x, float y) {
|
||||||
|
this.pos.x += x;
|
||||||
|
this.pos.y += y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotate(float yaw, float pitch) {
|
||||||
|
this.yaw += yaw;
|
||||||
|
this.pitch += pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getPos() {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getYaw() {
|
||||||
|
return yaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPitch() {
|
||||||
|
return pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFov() {
|
||||||
|
return fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(float x) {
|
||||||
|
this.pos.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(float y) {
|
||||||
|
this.pos.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZ(float z) {
|
||||||
|
this.pos.z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setYaw(float yaw) {
|
||||||
|
this.yaw = yaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPitch(float pitch) {
|
||||||
|
this.pitch = pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
resetPosition();
|
||||||
|
resetRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPosition() {
|
||||||
|
pos = new Vector3f(0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetRotation() {
|
||||||
|
yaw = 0.0f;
|
||||||
|
pitch = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveForward(float distance) {
|
||||||
|
pos.x += distance * (float) Math.cos(yaw);
|
||||||
|
pos.y += distance * (float) Math.sin(yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveRight(float distance) {
|
||||||
|
pos.x += distance * (float) Math.cos(yaw);
|
||||||
|
pos.y += distance * (float) Math.sin(yaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveUp(float distance) {
|
||||||
|
pos.z += distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveDown(float distance) {
|
||||||
|
pos.z -= distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addYaw(float angle) {
|
||||||
|
yaw += angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPitch(float angle) {
|
||||||
|
pitch += angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(Vector3f pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(float yaw, float pitch) {
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4f getMatrix() {
|
||||||
|
Vector3f forward = new Vector3f(
|
||||||
|
(float) (Math.cos(yaw) * Math.cos(pitch)),
|
||||||
|
(float) (Math.sin(pitch)),
|
||||||
|
(float) (Math.sin(yaw) * Math.cos(pitch)));
|
||||||
|
|
||||||
|
return new Matrix4f()
|
||||||
|
.perspective((float) (Math.toRadians(fov)), aspect, zNear, zFar)
|
||||||
|
.lookAt(pos, forward, new Vector3f(0.0f, 1.0f, 0.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package chess.view.DDDrender.opengl;
|
package chess.view.render3D;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL30;
|
import org.lwjgl.opengl.GL30;
|
||||||
|
|
||||||
@@ -1,19 +1,30 @@
|
|||||||
package chess.view.DDDrender.loader;
|
package chess.view.render3D;
|
||||||
|
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
|
|
||||||
import chess.view.DDDrender.opengl.ElementBuffer;
|
import static org.lwjgl.opengl.GL30.*;
|
||||||
import chess.view.DDDrender.opengl.VertexArray;
|
import chess.view.render3D.shader.BoardShader;
|
||||||
import chess.view.DDDrender.opengl.VertexBuffer;
|
|
||||||
|
|
||||||
public class BoardModelLoader {
|
public class Renderer {
|
||||||
|
private BoardShader shader;
|
||||||
|
private VertexArray vao;
|
||||||
|
|
||||||
private static int BOARD_WIDTH = 8;
|
private static int BOARD_WIDTH = 8;
|
||||||
private static int BOARD_HEIGHT = 8;
|
private static int BOARD_HEIGHT = 8;
|
||||||
private static int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT;
|
private static int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT;
|
||||||
private static int SQUARE_VERTEX_COUNT = 4;
|
private static int SQUARE_VERTEX_COUNT = 4;
|
||||||
|
|
||||||
private static float[] GetBoardPositions() {
|
public Renderer() {
|
||||||
|
this.shader = new BoardShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init() {
|
||||||
|
shader.LoadShader();
|
||||||
|
InitBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float[] GetBoardPositions() {
|
||||||
float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
|
float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
|
||||||
for (int i = 0; i < BOARD_WIDTH; i++) {
|
for (int i = 0; i < BOARD_WIDTH; i++) {
|
||||||
for (int j = 0; j < BOARD_HEIGHT; j++) {
|
for (int j = 0; j < BOARD_HEIGHT; j++) {
|
||||||
@@ -47,12 +58,12 @@ public class BoardModelLoader {
|
|||||||
return positions;
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float[] GetBoardColors() {
|
private float[] GetBoardColors() {
|
||||||
float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
|
float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
|
||||||
for (int i = 0; i < BOARD_WIDTH; i++) {
|
for (int i = 0; i < BOARD_WIDTH; i++) {
|
||||||
for (int j = 0; j < BOARD_HEIGHT; j++) {
|
for (int j = 0; j < BOARD_HEIGHT; j++) {
|
||||||
Vector3f color;
|
Vector3f color;
|
||||||
if ((i + j) % 2 == 0) {
|
if ((i + j) % 2 != 0) {
|
||||||
color = new Vector3f(1.0f, 1.0f, 1.0f);
|
color = new Vector3f(1.0f, 1.0f, 1.0f);
|
||||||
} else {
|
} else {
|
||||||
color = new Vector3f(0.0f, 0.0f, 0.0f);
|
color = new Vector3f(0.0f, 0.0f, 0.0f);
|
||||||
@@ -68,12 +79,12 @@ public class BoardModelLoader {
|
|||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] GetBoardIndicies() {
|
private int[] GetBoardIndicies() {
|
||||||
int[] indices = new int[BOARD_SIZE * 6];
|
int[] indices = new int[BOARD_SIZE * 6];
|
||||||
for (int i = 0; i < BOARD_SIZE; i++) {
|
for (int i = 0; i < BOARD_SIZE; i++) {
|
||||||
indices[i * 6] = i * 4;
|
indices[i * 6] = i * 4;
|
||||||
indices[i * 6 + 1] = i * 4 + 2;
|
indices[i * 6 + 1] = i * 4 + 1;
|
||||||
indices[i * 6 + 2] = i * 4 + 1;
|
indices[i * 6 + 2] = i * 4 + 2;
|
||||||
indices[i * 6 + 3] = i * 4 + 1;
|
indices[i * 6 + 3] = i * 4 + 1;
|
||||||
indices[i * 6 + 4] = i * 4 + 2;
|
indices[i * 6 + 4] = i * 4 + 2;
|
||||||
indices[i * 6 + 5] = i * 4 + 3;
|
indices[i * 6 + 5] = i * 4 + 3;
|
||||||
@@ -81,9 +92,9 @@ public class BoardModelLoader {
|
|||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VertexArray GetBoardModel() {
|
private void InitBoard() {
|
||||||
ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies());
|
ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies());
|
||||||
VertexArray vao = new VertexArray(eBuffer);
|
this.vao = new VertexArray(eBuffer);
|
||||||
|
|
||||||
VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3);
|
VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3);
|
||||||
positionBuffer.AddVertexAttribPointer(0, 3, 0);
|
positionBuffer.AddVertexAttribPointer(0, 3, 0);
|
||||||
@@ -91,10 +102,22 @@ public class BoardModelLoader {
|
|||||||
VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3);
|
VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3);
|
||||||
colorBuffer.AddVertexAttribPointer(1, 3, 0);
|
colorBuffer.AddVertexAttribPointer(1, 3, 0);
|
||||||
|
|
||||||
vao.Bind();
|
this.vao.Bind();
|
||||||
vao.BindVertexBuffer(positionBuffer);
|
this.vao.BindVertexBuffer(positionBuffer);
|
||||||
vao.BindVertexBuffer(colorBuffer);
|
this.vao.BindVertexBuffer(colorBuffer);
|
||||||
vao.Unbind();
|
this.vao.Unbind();
|
||||||
return vao;
|
}
|
||||||
|
|
||||||
|
public void Render(Camera cam) {
|
||||||
|
this.shader.Start();
|
||||||
|
this.shader.SetCamMatrix(cam.getMatrix());
|
||||||
|
RenderVao(vao);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenderVao(VertexArray vertexArray) {
|
||||||
|
this.shader.Start();
|
||||||
|
vertexArray.Bind();
|
||||||
|
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
|
||||||
|
vertexArray.Unbind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package chess.view.DDDrender.opengl;
|
package chess.view.render3D;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -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.opengl;
|
package chess.view.render3D;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_FLOAT;
|
import static org.lwjgl.opengl.GL11.GL_FLOAT;
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ public class VertexBuffer {
|
|||||||
|
|
||||||
public void BindVertexAttribs() {
|
public void BindVertexAttribs() {
|
||||||
for (VertexAttribPointer vertexAttribPointer : vertexAttribs) {
|
for (VertexAttribPointer vertexAttribPointer : vertexAttribs) {
|
||||||
GL30.glEnableVertexAttribArray(vertexAttribPointer.index());
|
GL30.glEnableVertexAttribArray(vertexAttribPointer.index);
|
||||||
GL30.glVertexAttribPointer(vertexAttribPointer.index(), vertexAttribPointer.size(), GL_FLOAT, false,
|
GL30.glVertexAttribPointer(vertexAttribPointer.index, vertexAttribPointer.size, GL_FLOAT, false,
|
||||||
this.dataStride * 4, vertexAttribPointer.offset());
|
this.dataStride * 4, vertexAttribPointer.offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,11 @@
|
|||||||
package chess.view.DDDrender;
|
package chess.view.render3D;
|
||||||
|
|
||||||
import org.lwjgl.*;
|
import org.lwjgl.*;
|
||||||
import org.lwjgl.glfw.*;
|
import org.lwjgl.glfw.*;
|
||||||
import org.lwjgl.opengl.*;
|
import org.lwjgl.opengl.*;
|
||||||
import org.lwjgl.system.*;
|
import org.lwjgl.system.*;
|
||||||
|
|
||||||
import chess.model.Coordinate;
|
|
||||||
import chess.view.DDDrender.world.Entity;
|
|
||||||
import chess.view.DDDrender.world.World;
|
|
||||||
import common.Signal1;
|
|
||||||
|
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static org.lwjgl.glfw.Callbacks.*;
|
import static org.lwjgl.glfw.Callbacks.*;
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
@@ -29,32 +19,11 @@ public class Window {
|
|||||||
private long window;
|
private long window;
|
||||||
|
|
||||||
private Renderer renderer;
|
private Renderer renderer;
|
||||||
private final Camera cam;
|
private Camera cam;
|
||||||
private final World world;
|
|
||||||
|
|
||||||
private final Queue<Runnable> tasks;
|
public Window() {
|
||||||
private final List<Consumer<Float>> regularTasks;
|
|
||||||
|
|
||||||
public final Signal1<Coordinate> OnCellClick = new Signal1<>();
|
|
||||||
|
|
||||||
public Window(Renderer renderer, World world) {
|
|
||||||
this.renderer = new Renderer();
|
this.renderer = new Renderer();
|
||||||
this.cam = world.getCamera();
|
this.cam = new Camera();
|
||||||
this.tasks = new ConcurrentLinkedDeque<>();
|
|
||||||
this.world = world;
|
|
||||||
this.regularTasks = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRegularTask(Consumer<Float> task) {
|
|
||||||
this.regularTasks.add(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void scheduleTask(Runnable runnable) {
|
|
||||||
this.tasks.add(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Runnable getNextTask() {
|
|
||||||
return this.tasks.poll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -87,7 +56,7 @@ public class Window {
|
|||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
|
||||||
|
|
||||||
// Create the window
|
// Create the window
|
||||||
window = glfwCreateWindow(1000, 1000, "3DChess", NULL, NULL);
|
window = glfwCreateWindow(1000, 1000, "Chess4J", NULL, NULL);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
throw new RuntimeException("Failed to create the GLFW window");
|
throw new RuntimeException("Failed to create the GLFW window");
|
||||||
|
|
||||||
@@ -118,29 +87,9 @@ public class Window {
|
|||||||
glfwShowWindow(window);
|
glfwShowWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void render(float delta, float aspectRatio) {
|
private void render() {
|
||||||
cam.setAspectRatio(aspectRatio);
|
cam.rotate(0.01f, 0.01f);
|
||||||
renderer.Update(cam);
|
renderer.Render(cam);
|
||||||
renderer.RenderBoard();
|
|
||||||
renderWorld();
|
|
||||||
// renderPieces();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderWorld() {
|
|
||||||
for (Entity entity : this.world.getEntites()) {
|
|
||||||
entity.render(this.renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeTasks(float delta) {
|
|
||||||
Runnable task = getNextTask();
|
|
||||||
while (task != null) {
|
|
||||||
task.run();
|
|
||||||
task = getNextTask();
|
|
||||||
}
|
|
||||||
for (Consumer<Float> consumer : regularTasks) {
|
|
||||||
consumer.accept(delta);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loop() {
|
private void loop() {
|
||||||
@@ -154,31 +103,16 @@ public class Window {
|
|||||||
renderer.Init();
|
renderer.Init();
|
||||||
|
|
||||||
// Set the clear color
|
// Set the clear color
|
||||||
glClearColor(0.4f, 0.4f, 0.6f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glCullFace(GL_FRONT);
|
|
||||||
glFrontFace(GL_CW);
|
|
||||||
|
|
||||||
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
double lastTime = glfwGetTime();
|
|
||||||
|
|
||||||
int width[] = new int[1];
|
|
||||||
int height[] = new int[1];
|
|
||||||
glfwGetWindowSize(window, width, height);
|
|
||||||
|
|
||||||
// Run the rendering loop until the user has attempted to close
|
// Run the rendering loop until the user has attempted to close
|
||||||
// the window or has pressed the ESCAPE key.
|
// the window or has pressed the ESCAPE key.
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
|
||||||
|
|
||||||
double currentTime = glfwGetTime();
|
render();
|
||||||
float deltaTime = (float) (currentTime - lastTime);
|
|
||||||
render(deltaTime, (float) width[0] / (float) height[0]);
|
|
||||||
lastTime = glfwGetTime();
|
|
||||||
|
|
||||||
glfwSwapBuffers(window); // swap the color buffers
|
glfwSwapBuffers(window); // swap the color buffers
|
||||||
|
|
||||||
@@ -186,11 +120,15 @@ public class Window {
|
|||||||
// invoked during this call.
|
// invoked during this call.
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
executeTasks(deltaTime);
|
try (MemoryStack stack = stackPush()) {
|
||||||
|
IntBuffer pWidth = stack.mallocInt(1); // int*
|
||||||
|
IntBuffer pHeight = stack.mallocInt(1); // int*
|
||||||
|
|
||||||
glfwGetWindowSize(window, width, height);
|
// Get the window size passed to glfwCreateWindow
|
||||||
glViewport(0, 0, width[0], height[0]);
|
glfwGetWindowSize(window, pWidth, pHeight);
|
||||||
|
|
||||||
|
glViewport(0, 0, pWidth.get(), pHeight.get());
|
||||||
|
} // the stack frame is popped automatically
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
@@ -56,8 +56,8 @@ public abstract class ShaderProgram {
|
|||||||
|
|
||||||
if (compileSuccesful.get() != 1) {
|
if (compileSuccesful.get() != 1) {
|
||||||
System.out.println("Shader did not compile !");
|
System.out.println("Shader did not compile !");
|
||||||
System.err.println(GL30.glGetShaderInfoLog(shaderId));
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shaderId;
|
return shaderId;
|
||||||
@@ -68,7 +68,7 @@ public abstract class ShaderProgram {
|
|||||||
protected int GetUniformLocation(String uniformName) {
|
protected int GetUniformLocation(String uniformName) {
|
||||||
int location = GL30.glGetUniformLocation(programId, uniformName);
|
int location = GL30.glGetUniformLocation(programId, uniformName);
|
||||||
if (location == -1) {
|
if (location == -1) {
|
||||||
System.out.println("Uniform value \"" + uniformName + "\" not found !");
|
System.out.println("Uniform value not found !");
|
||||||
}
|
}
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
@@ -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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user