diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 88cd949..7b909ba 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -43,13 +43,15 @@ public final class App extends Application { scene.getRoot(); + stage.setMinWidth(1080); + stage.setMinHeight(720); stage.setOnCloseRequest(event -> { event.consume(); startQuit(); }); stage.setScene(scene); - stage.setResizable(false); + stage.setResizable(true); stage.show(); diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index 00003b9..00f91cc 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -123,6 +123,7 @@ public final class Server { information.players[0].name = user; information.players[0].isHuman = false; information.players[0].computerDifficulty = 5; + information.players[0].computerThinkTime = 1; information.players[1].name = response.opponent(); Runnable onGameOverRunnable = isSingleGame.get()? null: this::gameOver; diff --git a/app/src/main/java/org/toop/app/canvas/Connect4Canvas.java b/app/src/main/java/org/toop/app/canvas/Connect4Canvas.java index 913bb62..c17dafd 100644 --- a/app/src/main/java/org/toop/app/canvas/Connect4Canvas.java +++ b/app/src/main/java/org/toop/app/canvas/Connect4Canvas.java @@ -6,6 +6,6 @@ import java.util.function.Consumer; public class Connect4Canvas extends GameCanvas { public Connect4Canvas(Color color, int width, int height, Consumer onCellClicked) { - super(color, Color.TRANSPARENT, width, height, 7, 6, 10, true, onCellClicked); + super(color, Color.TRANSPARENT, width, height, 7, 6, 10, true, onCellClicked,null); } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java index 1a002a6..f8f6d70 100644 --- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -8,6 +8,7 @@ import javafx.scene.input.MouseButton; import javafx.scene.paint.Color; import javafx.util.Duration; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; public abstract class GameCanvas { @@ -35,7 +36,7 @@ public abstract class GameCanvas { protected final Cell[] cells; - protected GameCanvas(Color color, Color backgroundColor, int width, int height, int rowSize, int columnSize, int gapSize, boolean edges, Consumer onCellClicked) { + protected GameCanvas(Color color, Color backgroundColor, int width, int height, int rowSize, int columnSize, int gapSize, boolean edges, Consumer onCellClicked, Consumer newCellEntered) { canvas = new Canvas(width, height); graphics = canvas.getGraphicsContext2D(); @@ -81,6 +82,9 @@ public abstract class GameCanvas { } }); + + + render(); } @@ -150,6 +154,21 @@ public abstract class GameCanvas { graphics.fillOval(x, y, width, height); } + public void drawInnerDot(Color color, int cell, boolean slightlyBigger) { + final float x = cells[cell].x() + gapSize; + final float y = cells[cell].y() + gapSize; + + float multiplier = slightlyBigger?1.4f:1.5f; + + final float width = (cells[cell].width() - gapSize * 2)/multiplier; + final float height = (cells[cell].height() - gapSize * 2)/multiplier; + + float offset = slightlyBigger?5f:4f; + + graphics.setFill(color); + graphics.fillOval(x + width/offset, y + height/offset, width, height); + } + private void drawDotScaled(Color color, int cell, double scale) { final float cx = cells[cell].x() + gapSize; final float cy = cells[cell].y() + gapSize; diff --git a/app/src/main/java/org/toop/app/canvas/ReversiCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiCanvas.java index 9b9cecf..b3f1628 100644 --- a/app/src/main/java/org/toop/app/canvas/ReversiCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/ReversiCanvas.java @@ -1,15 +1,68 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; +import org.toop.game.records.Move; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; public final class ReversiCanvas extends GameCanvas { - public ReversiCanvas(Color color, int width, int height, Consumer onCellClicked) { - super(color, Color.GREEN, width, height, 8, 8, 5, true, onCellClicked); + private Move[] currentlyHighlightedMoves = null; + public ReversiCanvas(Color color, int width, int height, Consumer onCellClicked, Consumer newCellEntered) { + super(color, new Color(0f,0.4f,0.2f,1f), width, height, 8, 8, 5, true, onCellClicked, newCellEntered); drawStartingDots(); + + final AtomicReference lastHoveredCell = new AtomicReference<>(null); + + canvas.setOnMouseMoved(event -> { + double mouseX = event.getX(); + double mouseY = event.getY(); + int cellId = -1; + + Cell hovered = null; + for (Cell cell : cells) { + if (cell.isInside(mouseX, mouseY)) { + hovered = cell; + cellId = turnCoordsIntoCellId(mouseX, mouseY); + break; + } + } + + Cell previous = lastHoveredCell.get(); + + if (hovered != previous) { + lastHoveredCell.set(hovered); + newCellEntered.accept(cellId); + } + }); } + public void setCurrentlyHighlightedMovesNull() { + currentlyHighlightedMoves = null; + } + + public void drawHighlightDots(Move[] moves){ + if (currentlyHighlightedMoves != null){ + for (final Move move : currentlyHighlightedMoves){ + Color color = move.value() == 'W'? Color.BLACK: Color.WHITE; + drawInnerDot(color, move.position(), true); + } + } + currentlyHighlightedMoves = moves; + if (moves != null) { + for (Move move : moves) { + Color color = move.value() == 'B' ? Color.BLACK : Color.WHITE; + drawInnerDot(color, move.position(), false); + } + } + } + + private int turnCoordsIntoCellId(double x, double y) { + final int column = (int) ((x / this.width) * rowSize); + final int row = (int) ((y / this.height) * columnSize); + return column + row * rowSize; + } + public void drawStartingDots() { drawDot(Color.BLACK, 28); drawDot(Color.WHITE, 36); @@ -17,7 +70,15 @@ public final class ReversiCanvas extends GameCanvas { drawDot(Color.WHITE, 27); } - public void drawLegalPosition(Color color, int cell) { - drawDot(new Color(color.getRed(), color.getGreen(), color.getBlue(), 0.25), cell); + public void drawLegalPosition(int cell, char player) { + + Color innerColor; + if (player == 'B') { + innerColor = new Color(0.0f, 0.0f, 0.0f, 0.6f); + } + else { + innerColor = new Color(1.0f, 1.0f, 1.0f, 0.75f); + } + drawInnerDot(innerColor, cell,false); } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java index d7ccbc8..890eb39 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java @@ -6,7 +6,7 @@ import java.util.function.Consumer; public final class TicTacToeCanvas extends GameCanvas { public TicTacToeCanvas(Color color, int width, int height, Consumer onCellClicked) { - super(color, Color.TRANSPARENT, width, height, 3, 3, 30, false, onCellClicked); + super(color, Color.TRANSPARENT, width, height, 3, 3, 30, false, onCellClicked,null); } public void drawX(Color color, int cell) { diff --git a/app/src/main/java/org/toop/app/game/Connect4Game.java b/app/src/main/java/org/toop/app/game/Connect4Game.java index 8774fd7..76bd13e 100644 --- a/app/src/main/java/org/toop/app/game/Connect4Game.java +++ b/app/src/main/java/org/toop/app/game/Connect4Game.java @@ -12,7 +12,8 @@ import org.toop.framework.eventbus.EventFlow; import org.toop.framework.networking.events.NetworkEvents; import org.toop.game.Connect4.Connect4; import org.toop.game.Connect4.Connect4AI; -import org.toop.game.Game; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -24,7 +25,7 @@ public class Connect4Game { private final int myTurn; private Runnable onGameOver; - private final BlockingQueue moveQueue; + private final BlockingQueue moveQueue; private final Connect4 game; private final Connect4AI ai; @@ -40,7 +41,7 @@ public class Connect4Game { this.information = information; this.myTurn = myTurn; this.onGameOver = onGameOver; - moveQueue = new LinkedBlockingQueue(); + moveQueue = new LinkedBlockingQueue(); game = new Connect4(); @@ -68,7 +69,7 @@ public class Connect4Game { final char value = game.getCurrentTurn() == 0? 'X' : 'O'; try { - moveQueue.put(new Game.Move(cell%columnSize, value)); + moveQueue.put(new Move(cell%columnSize, value)); } catch (InterruptedException _) {} } } else { @@ -76,7 +77,7 @@ public class Connect4Game { final char value = myTurn == 0? 'X' : 'O'; try { - moveQueue.put(new Game.Move(cell%columnSize, value)); + moveQueue.put(new Move(cell%columnSize, value)); } catch (InterruptedException _) {} } } @@ -113,14 +114,14 @@ public class Connect4Game { currentValue, information.players[nextTurn].name); - Game.Move move = null; + Move move = null; if (information.players[currentTurn].isHuman) { try { - final Game.Move wants = moveQueue.take(); - final Game.Move[] legalMoves = game.getLegalMoves(); + final Move wants = moveQueue.take(); + final Move[] legalMoves = game.getLegalMoves(); - for (final Game.Move legalMove : legalMoves) { + for (final Move legalMove : legalMoves) { if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) { move = wants; @@ -147,7 +148,7 @@ public class Connect4Game { continue; } - final Game.State state = game.play(move); + final GameState state = game.play(move); updateCanvas(); /* if (move.value() == 'X') { @@ -156,10 +157,10 @@ public class Connect4Game { canvas.drawO(Color.ROYALBLUE, move.position()); } */ - if (state != Game.State.NORMAL) { - if (state == Game.State.WIN) { + if (state != GameState.NORMAL) { + if (state == GameState.WIN) { view.gameOver(true, information.players[currentTurn].name); - } else if (state == Game.State.DRAW) { + } else if (state == GameState.DRAW) { view.gameOver(false, ""); } @@ -181,11 +182,11 @@ public class Connect4Game { playerChar = myTurn == 0? 'O' : 'X'; } - final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); - final Game.State state = game.play(move); + final Move move = new Move(Integer.parseInt(response.move()), playerChar); + final GameState state = game.play(move); - if (state != Game.State.NORMAL) { - if (state == Game.State.WIN) { + if (state != GameState.NORMAL) { + if (state == GameState.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { view.gameOver(true, information.players[0].name); gameOver(); @@ -193,7 +194,7 @@ public class Connect4Game { view.gameOver(false, information.players[1].name); gameOver(); } - } else if (state == Game.State.DRAW) { + } else if (state == GameState.DRAW) { view.gameOver(false, ""); gameOver(); } @@ -232,7 +233,7 @@ public class Connect4Game { position = moveQueue.take().position(); } catch (InterruptedException _) {} } else { - final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty); + final Move move = ai.findBestMove(game, information.players[0].computerDifficulty); assert move != null; position = move.position(); @@ -253,10 +254,10 @@ public class Connect4Game { private void updateCanvas() { canvas.clearAll(); - for (int i = 0; i < game.board.length; i++) { - if (game.board[i] == 'X') { + for (int i = 0; i < game.getBoard().length; i++) { + if (game.getBoard()[i] == 'X') { canvas.drawDot(Color.RED, i); - } else if (game.board[i] == 'O') { + } else if (game.getBoard()[i] == 'O') { canvas.drawDot(Color.BLUE, i); } } diff --git a/app/src/main/java/org/toop/app/game/ReversiGame.java b/app/src/main/java/org/toop/app/game/ReversiGame.java index 430cf63..b70e6ab 100644 --- a/app/src/main/java/org/toop/app/game/ReversiGame.java +++ b/app/src/main/java/org/toop/app/game/ReversiGame.java @@ -8,13 +8,15 @@ import org.toop.app.widget.WidgetContainer; import org.toop.app.widget.view.GameView; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.networking.events.NetworkEvents; -import org.toop.game.Game; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import org.toop.game.reversi.Reversi; import org.toop.game.reversi.ReversiAI; import javafx.geometry.Pos; import javafx.scene.paint.Color; +import java.awt.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -69,7 +71,7 @@ public final class ReversiGame { final char value = game.getCurrentTurn() == 0? 'B' : 'W'; try { - moveQueue.put(new Game.Move(cell, value)); + moveQueue.put(new Move(cell, value)); } catch (InterruptedException _) {} } } else { @@ -77,11 +79,13 @@ public final class ReversiGame { final char value = myTurn == 0? 'B' : 'W'; try { - moveQueue.put(new Game.Move(cell, value)); + moveQueue.put(new Move(cell, value)); } catch (InterruptedException _) {} } } - }); + },this::highlightCells); + + primary.add(Pos.CENTER, canvas.getCanvas()); WidgetContainer.getCurrentView().transitionNext(primary); @@ -123,14 +127,14 @@ public final class ReversiGame { currentValue, information.players[nextTurn].name); - Game.Move move = null; + Move move = null; if (information.players[currentTurn].isHuman) { try { - final Game.Move wants = moveQueue.take(); - final Game.Move[] legalMoves = game.getLegalMoves(); + final Move wants = moveQueue.take(); + final Move[] legalMoves = game.getLegalMoves(); - for (final Game.Move legalMove : legalMoves) { + for (final Move legalMove : legalMoves) { if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) { move = wants; @@ -157,13 +161,18 @@ public final class ReversiGame { continue; } - final Game.State state = game.play(move); + canvas.setCurrentlyHighlightedMovesNull(); + final GameState state = game.play(move); updateCanvas(true); - if (state != Game.State.NORMAL) { - if (state == Game.State.WIN) { - primary.gameOver(true, information.players[currentTurn].name); - } else if (state == Game.State.DRAW) { + if (state != GameState.NORMAL) { + if (state == GameState.TURN_SKIPPED){ + continue; + } + int winningPLayerNumber = getPlayerNumberWithHighestScore(); + if (state == GameState.WIN && winningPLayerNumber > -1) { + primary.gameOver(true, information.players[winningPLayerNumber].name); + } else if (state == GameState.DRAW || winningPLayerNumber == -1) { primary.gameOver(false, ""); } @@ -172,6 +181,13 @@ public final class ReversiGame { } } + private int getPlayerNumberWithHighestScore(){ + Reversi.Score score = game.getScore(); + if (score.player1Score() > score.player2Score()) return 0; + if (score.player1Score() < score.player2Score()) return 1; + return -1; + } + private void onMoveResponse(NetworkEvents.GameMoveResponse response) { if (!isRunning.get()) { return; @@ -185,11 +201,11 @@ public final class ReversiGame { playerChar = myTurn == 0? 'W' : 'B'; } - final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); - final Game.State state = game.play(move); + final Move move = new Move(Integer.parseInt(response.move()), playerChar); + final GameState state = game.play(move); - if (state != Game.State.NORMAL) { - if (state == Game.State.WIN) { + if (state != GameState.NORMAL) { + if (state == GameState.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { primary.gameOver(true, information.players[0].name); gameOver(); @@ -229,7 +245,7 @@ public final class ReversiGame { position = moveQueue.take().position(); } catch (InterruptedException _) {} } else { - final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty); + final Move move = ai.findBestMove(game, information.players[0].computerDifficulty); assert move != null; position = move.position(); @@ -243,15 +259,15 @@ public final class ReversiGame { // Todo: this is very inefficient. still very fast but if the grid is bigger it might cause issues. improve. canvas.clearAll(); - for (int i = 0; i < game.board.length; i++) { - if (game.board[i] == 'B') { + for (int i = 0; i < game.getBoard().length; i++) { + if (game.getBoard()[i] == 'B') { canvas.drawDot(Color.BLACK, i); - } else if (game.board[i] == 'W') { + } else if (game.getBoard()[i] == 'W') { canvas.drawDot(Color.WHITE, i); } } - final Game.Move[] flipped = game.getMostRecentlyFlippedPieces(); + final Move[] flipped = game.getMostRecentlyFlippedPieces(); final SequentialTransition animation = new SequentialTransition(); isPaused.set(true); @@ -260,7 +276,7 @@ public final class ReversiGame { final Color toColor = game.getCurrentPlayer() == 'W'? Color.BLACK : Color.WHITE; if (animate && flipped != null) { - for (final Game.Move flip : flipped) { + for (final Move flip : flipped) { canvas.clear(flip.position()); canvas.drawDot(fromColor, flip.position()); animation.getChildren().addFirst(canvas.flipDot(fromColor, toColor, flip.position())); @@ -270,11 +286,13 @@ public final class ReversiGame { animation.setOnFinished(_ -> { isPaused.set(false); - final Game.Move[] legalMoves = game.getLegalMoves(); + if (information.players[game.getCurrentTurn()].isHuman) { + final Move[] legalMoves = game.getLegalMoves(); - for (final Game.Move legalMove : legalMoves) { - canvas.drawLegalPosition(fromColor, legalMove.position()); - } + for (final Game.Move legalMove : legalMoves) { + canvas.drawLegalPosition(legalMove.position(), game.getCurrentPlayer()); + } + } }); animation.play(); @@ -289,4 +307,27 @@ public final class ReversiGame { currentValue, information.players[isMe? 1 : 0].name); } + + private void highlightCells(int cellEntered) { + if (information.players[game.getCurrentTurn()].isHuman) { + Move[] legalMoves = game.getLegalMoves(); + boolean isLegalMove = false; + for (Move move : legalMoves) { + if (move.position() == cellEntered){ + isLegalMove = true; + break; + } + } + + if (cellEntered >= 0){ + Move[] moves = null; + if (isLegalMove) { + moves = game.getFlipsForPotentialMove( + new Point(cellEntered%game.getColumnSize(),cellEntered/game.getRowSize()), + game.getCurrentPlayer()); + } + canvas.drawHighlightDots(moves); + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/game/TicTacToeGame.java b/app/src/main/java/org/toop/app/game/TicTacToeGame.java index 2c4fb1e..427d09f 100644 --- a/app/src/main/java/org/toop/app/game/TicTacToeGame.java +++ b/app/src/main/java/org/toop/app/game/TicTacToeGame.java @@ -7,7 +7,8 @@ import org.toop.app.widget.WidgetContainer; import org.toop.app.widget.view.GameView; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.networking.events.NetworkEvents; -import org.toop.game.Game; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import org.toop.game.tictactoe.TicTacToe; import org.toop.game.tictactoe.TicTacToeAI; @@ -39,7 +40,7 @@ public final class TicTacToeGame { this.myTurn = myTurn; this.onGameOver = onGameOver; - moveQueue = new LinkedBlockingQueue(); + moveQueue = new LinkedBlockingQueue(); game = new TicTacToe(); ai = new TicTacToeAI(); @@ -66,7 +67,7 @@ public final class TicTacToeGame { final char value = game.getCurrentTurn() == 0? 'X' : 'O'; try { - moveQueue.put(new Game.Move(cell, value)); + moveQueue.put(new Move(cell, value)); } catch (InterruptedException _) {} } } else { @@ -74,7 +75,7 @@ public final class TicTacToeGame { final char value = myTurn == 0? 'X' : 'O'; try { - moveQueue.put(new Game.Move(cell, value)); + moveQueue.put(new Move(cell, value)); } catch (InterruptedException _) {} } } @@ -109,14 +110,14 @@ public final class TicTacToeGame { currentValue, information.players[nextTurn].name); - Game.Move move = null; + Move move = null; if (information.players[currentTurn].isHuman) { try { - final Game.Move wants = moveQueue.take(); - final Game.Move[] legalMoves = game.getLegalMoves(); + final Move wants = moveQueue.take(); + final Move[] legalMoves = game.getLegalMoves(); - for (final Game.Move legalMove : legalMoves) { + for (final Move legalMove : legalMoves) { if (legalMove.position() == wants.position() && legalMove.value() == wants.value()) { move = wants; @@ -143,7 +144,7 @@ public final class TicTacToeGame { continue; } - final Game.State state = game.play(move); + final GameState state = game.play(move); if (move.value() == 'X') { canvas.drawX(Color.INDIANRED, move.position()); @@ -176,11 +177,11 @@ public final class TicTacToeGame { playerChar = myTurn == 0? 'O' : 'X'; } - final Game.Move move = new Game.Move(Integer.parseInt(response.move()), playerChar); - final Game.State state = game.play(move); + final Move move = new Move(Integer.parseInt(response.move()), playerChar); + final GameState state = game.play(move); - if (state != Game.State.NORMAL) { - if (state == Game.State.WIN) { + if (state != GameState.NORMAL) { + if (state == GameState.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { primary.gameOver(true, information.players[0].name); gameOver(); @@ -188,7 +189,7 @@ public final class TicTacToeGame { primary.gameOver(false, information.players[1].name); gameOver(); } - } else if (state == Game.State.DRAW) { + } else if (state == GameState.DRAW) { if(game.getLegalMoves().length == 0) { //only return draw in online multiplayer if the game is actually over. primary.gameOver(false, ""); gameOver(); @@ -227,13 +228,8 @@ public final class TicTacToeGame { position = moveQueue.take().position(); } catch (InterruptedException _) {} } else { - final Game.Move move; - if (information.players[1].name.equalsIgnoreCase("pism")) { - move = ai.findWorstMove(game,9); - }else{ - move = ai.findBestMove(game, information.players[0].computerDifficulty); - } - + final Move move; + move = ai.findBestMove(game, information.players[0].computerDifficulty); assert move != null; position = move.position(); } diff --git a/app/src/main/java/org/toop/app/view/displays/SongDisplay.java b/app/src/main/java/org/toop/app/view/displays/SongDisplay.java index 8e9da5d..3bd8a6c 100644 --- a/app/src/main/java/org/toop/app/view/displays/SongDisplay.java +++ b/app/src/main/java/org/toop/app/view/displays/SongDisplay.java @@ -20,6 +20,7 @@ public class SongDisplay extends VBox implements Widget { private final Text songTitle; private final ProgressBar progressBar; private final Text progressText; + private boolean paused = false; public SongDisplay() { new EventFlow() @@ -28,7 +29,6 @@ public class SongDisplay extends VBox implements Widget { setAlignment(Pos.CENTER); getStyleClass().add("song-display"); - // TODO ADD GOOD SONG TITLES WITH ARTISTS DISPLAYED songTitle = new Text("song playing"); songTitle.getStyleClass().add("song-title"); @@ -38,8 +38,6 @@ public class SongDisplay extends VBox implements Widget { progressText = new Text("0:00/0:00"); progressText.getStyleClass().add("progress-text"); - // TODO ADD BETTER CSS FOR THE SKIPBUTTON WHERE ITS AT A NICER POSITION - Button skipButton = new Button(">>"); Button pauseButton = new Button("⏸"); Button previousButton = new Button("<<"); @@ -50,20 +48,20 @@ public class SongDisplay extends VBox implements Widget { skipButton.setOnAction( event -> { GlobalEventBus.post(new AudioEvents.SkipMusic()); + paused = false; + pauseButton.setText(getPlayString(paused)); }); pauseButton.setOnAction(event -> { GlobalEventBus.post(new AudioEvents.PauseMusic()); - if (pauseButton.getText().equals("⏸")) { - pauseButton.setText("▶"); - } - else if (pauseButton.getText().equals("▶")) { - pauseButton.setText("⏸"); - } + paused = !paused; + pauseButton.setText(getPlayString(paused)); }); previousButton.setOnAction( event -> { GlobalEventBus.post(new AudioEvents.PreviousMusic()); + paused = false; + pauseButton.setText(getPlayString(paused)); }); HBox control = new HBox(10, previousButton, pauseButton, skipButton); @@ -114,6 +112,14 @@ public class SongDisplay extends VBox implements Widget { public Node getNode() { return this; } + private String getPlayString(boolean paused) { + if (paused) { + return "▶"; + } + else { + return "⏸"; + } + } } diff --git a/app/src/main/java/org/toop/app/view/views/GameView.java b/app/src/main/java/org/toop/app/view/views/GameView.java index df69240..5b9b1d6 100644 --- a/app/src/main/java/org/toop/app/view/views/GameView.java +++ b/app/src/main/java/org/toop/app/view/views/GameView.java @@ -15,10 +15,12 @@ import javafx.scene.text.Text; import java.util.function.Consumer; public final class GameView extends View { + // TODO: This should be it's own file... private static class GameOverView extends View { private final boolean iWon; private final String winner; + // TODO: Make winner generic, there is no "I won" unless you play online or against bot. Should be a generic "... won" to simplify public GameOverView(boolean iWon, String winner) { super(false, "bg-popup"); @@ -71,6 +73,8 @@ public final class GameView extends View { private final Text nextPlayerHeader; + private final Text gameStateFeedback = text(); + private final ListView chatListView; private final TextField chatInput; @@ -112,34 +116,24 @@ public final class GameView extends View { exitButton.setText(AppContext.getString("exit")); exitButton.setOnAction(_ -> onExit.run()); - currentPlayerHeader = header("", "current-player"); + currentPlayerHeader = header("", "header"); currentMoveHeader = header(); - nextPlayerHeader = header(); } @Override public void setup() { - add(Pos.TOP_RIGHT, - fit(vboxFill( - currentPlayerHeader, - - hboxFill( - separator(), - currentMoveHeader, - separator() - ), - - nextPlayerHeader - )) + add( + Pos.TOP_CENTER, + gameStateFeedback ); add(Pos.BOTTOM_LEFT, - vboxFill( - forfeitButton, - exitButton - ) - ); + vboxFill( + forfeitButton, + exitButton + ) + ); if (chatListView != null) { add(Pos.BOTTOM_RIGHT, @@ -153,6 +147,7 @@ public final class GameView extends View { public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) { Platform.runLater(() -> { + gameStateFeedback.setText("Waiting on " + currentPlayer + " to make their move."); currentPlayerHeader.setText(currentPlayer); currentMoveHeader.setText(currentMove); diff --git a/app/src/main/java/org/toop/local/AppContext.java b/app/src/main/java/org/toop/local/AppContext.java index 84326e0..0097473 100644 --- a/app/src/main/java/org/toop/local/AppContext.java +++ b/app/src/main/java/org/toop/local/AppContext.java @@ -1,5 +1,10 @@ package org.toop.local; +import java.util.Locale; +import java.util.MissingResourceException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.toop.framework.resource.ResourceManager; import org.toop.framework.resource.resources.LocalizationAsset; @@ -15,6 +20,7 @@ public class AppContext { private static Locale locale = Locale.forLanguageTag("en"); private static final ObjectProperty localeProperty = new SimpleObjectProperty<>(locale); + private static final Logger logger = LogManager.getLogger(AppContext.class); public static LocalizationAsset getLocalization() { return localization; @@ -30,7 +36,26 @@ public class AppContext { } public static String getString(String key) { - return localization.getString(key, locale); + assert localization != null; + + // TODO: Gebruik ResourceBundle.getBundle() zodat de fallback automatisch gaat. + // Hiervoor zou de assetManager aangepast moeten worden. + + try{ // Main return + return localization.getString(key, locale); + } + catch (MissingResourceException e) { + logger.error("Missing resource key: {}, in bundle: {}. ", key, locale, e); + } + + try{ // Fallback return + return localization.getString(key, localization.getFallback()); + } + catch (MissingResourceException e) { + logger.error("Missing resource key: {}, in default bundle!", key, e); + } + // Default return + return "MISSING RESOURCE"; } public static StringBinding bindToKey(String key) { diff --git a/app/src/main/resources/assets/audio/music/formerseas.mp3 b/app/src/main/resources/assets/audio/music/DM DOKURO - Guardian of the Former Seas.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/formerseas.mp3 rename to app/src/main/resources/assets/audio/music/DM DOKURO - Guardian of the Former Seas.mp3 diff --git a/app/src/main/resources/assets/audio/music/Roar Of The Jungle Dragon - Terraria Calamity.mp3 b/app/src/main/resources/assets/audio/music/DM DOKURO - Roar of the Jungle Dragon.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/Roar Of The Jungle Dragon - Terraria Calamity.mp3 rename to app/src/main/resources/assets/audio/music/DM DOKURO - Roar of the Jungle Dragon.mp3 diff --git a/app/src/main/resources/assets/audio/music/extraction-point.mp3 b/app/src/main/resources/assets/audio/music/Hans Zimmer - Extraction Point.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/extraction-point.mp3 rename to app/src/main/resources/assets/audio/music/Hans Zimmer - Extraction Point.mp3 diff --git a/app/src/main/resources/assets/audio/music/damned.mp3 b/app/src/main/resources/assets/audio/music/Kevin Sherwood - Damned.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/damned.mp3 rename to app/src/main/resources/assets/audio/music/Kevin Sherwood - Damned.mp3 diff --git a/app/src/main/resources/assets/audio/music/MW Main Menu - some artist.mp3 b/app/src/main/resources/assets/audio/music/MW2 (2009) Multiplayer Theme.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/MW Main Menu - some artist.mp3 rename to app/src/main/resources/assets/audio/music/MW2 (2009) Multiplayer Theme.mp3 diff --git a/app/src/main/resources/assets/audio/music/godfrey.mp3 b/app/src/main/resources/assets/audio/music/Tai Tomisawa - Godfrey, First Elden Lord .mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/godfrey.mp3 rename to app/src/main/resources/assets/audio/music/Tai Tomisawa - Godfrey, First Elden Lord .mp3 diff --git a/app/src/main/resources/assets/audio/music/gladius.mp3 b/app/src/main/resources/assets/audio/music/bignic - Gladius.mp3 similarity index 100% rename from app/src/main/resources/assets/audio/music/gladius.mp3 rename to app/src/main/resources/assets/audio/music/bignic - Gladius.mp3 diff --git a/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 b/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 deleted file mode 100644 index 7105fde..0000000 Binary files a/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 and /dev/null differ diff --git a/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java index 3ec3328..7b5ec84 100644 --- a/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java +++ b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java @@ -63,6 +63,9 @@ public class LocalizationAsset extends BaseResource implements LoadableResource, loaded = false; } + /** Returns the fallback locale used when locale is missing argument*/ + public Locale getFallback() {return this.fallback;} + /** * Checks whether this asset has been loaded. * @@ -126,7 +129,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource, public void loadFile(File file) { try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { - Locale locale = extractLocale(file.getName(), baseName); + Locale locale = extractLocale(file.getName()); bundles.put(locale, new PropertyResourceBundle(reader)); } catch (IOException e) { throw new RuntimeException("Failed to load localization file: " + file, e); @@ -166,10 +169,9 @@ public class LocalizationAsset extends BaseResource implements LoadableResource, * Extracts a locale from a file name based on the pattern "base_LOCALE.properties". * * @param fileName the file name - * @param baseName the base name * @return extracted locale, or fallback if none found */ - private Locale extractLocale(String fileName, String baseName) { + private Locale extractLocale(String fileName) { int underscoreIndex = fileName.indexOf('_'); int dotIndex = fileName.lastIndexOf('.'); if (underscoreIndex > 0 && dotIndex > underscoreIndex) { diff --git a/game/src/main/java/org/toop/game/AI.java b/game/src/main/java/org/toop/game/AI.java index 0506b10..3f8fac4 100644 --- a/game/src/main/java/org/toop/game/AI.java +++ b/game/src/main/java/org/toop/game/AI.java @@ -1,5 +1,6 @@ package org.toop.game; -public abstract class AI { - public abstract Game.Move findBestMove(T game, int depth); +import org.toop.game.interfaces.IAIMove; + +public abstract class AI implements IAIMove { } \ No newline at end of file diff --git a/game/src/main/java/org/toop/game/Connect4/Connect4.java b/game/src/main/java/org/toop/game/Connect4/Connect4.java index 45f776d..7eb9266 100644 --- a/game/src/main/java/org/toop/game/Connect4/Connect4.java +++ b/game/src/main/java/org/toop/game/Connect4/Connect4.java @@ -1,6 +1,8 @@ package org.toop.game.Connect4; import org.toop.game.TurnBasedGame; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import java.util.ArrayList; @@ -9,7 +11,7 @@ public class Connect4 extends TurnBasedGame { public Connect4() { super(6, 7, 2); - movesLeft = board.length; + movesLeft = this.getBoard().length; } public Connect4(Connect4 other) { @@ -22,8 +24,8 @@ public class Connect4 extends TurnBasedGame { final ArrayList legalMoves = new ArrayList<>(); final char currentValue = getCurrentValue(); - for (int i = 0; i < columnSize; i++) { - if (board[i] == EMPTY) { + for (int i = 0; i < this.getColumnSize(); i++) { + if (this.getBoard()[i] == EMPTY) { legalMoves.add(new Move(i, currentValue)); } } @@ -31,63 +33,63 @@ public class Connect4 extends TurnBasedGame { } @Override - public State play(Move move) { + public GameState play(Move move) { assert move != null; - assert move.position() >= 0 && move.position() < board.length; + assert move.position() >= 0 && move.position() < this.getBoard().length; assert move.value() == getCurrentValue(); int lowestEmptySpot = move.position(); - for (int i = 0; i < rowSize; i++) { - int checkMovePosition = move.position() + columnSize * i; - if (checkMovePosition < board.length) { - if (board[checkMovePosition] == EMPTY) { + for (int i = 0; i < this.getRowSize(); i++) { + int checkMovePosition = move.position() + this.getColumnSize() * i; + if (checkMovePosition < this.getBoard().length) { + if (this.getBoard()[checkMovePosition] == EMPTY) { lowestEmptySpot = checkMovePosition; } } } - board[lowestEmptySpot] = move.value(); + this.getBoard()[lowestEmptySpot] = move.value(); movesLeft--; if (checkForWin()) { - return State.WIN; + return GameState.WIN; } nextTurn(); - return State.NORMAL; + return GameState.NORMAL; } private boolean checkForWin() { char[][] boardGrid = makeBoardAGrid(); - for (int row = 0; row < rowSize; row++) { - for (int col = 0; col < columnSize; col++) { + for (int row = 0; row < this.getRowSize(); row++) { + for (int col = 0; col < this.getColumnSize(); col++) { char cell = boardGrid[row][col]; if (cell == ' ' || cell == 0) continue; - if (col + 3 < columnSize && + if (col + 3 < this.getColumnSize() && cell == boardGrid[row][col + 1] && cell == boardGrid[row][col + 2] && cell == boardGrid[row][col + 3]) { return true; } - if (row + 3 < rowSize && + if (row + 3 < this.getRowSize() && cell == boardGrid[row + 1][col] && cell == boardGrid[row + 2][col] && cell == boardGrid[row + 3][col]) { return true; } - if (row + 3 < rowSize && col + 3 < columnSize && + if (row + 3 < this.getRowSize() && col + 3 < this.getColumnSize() && cell == boardGrid[row + 1][col + 1] && cell == boardGrid[row + 2][col + 2] && cell == boardGrid[row + 3][col + 3]) { return true; } - if (row + 3 < rowSize && col - 3 >= 0 && + if (row + 3 < this.getRowSize() && col - 3 >= 0 && cell == boardGrid[row + 1][col - 1] && cell == boardGrid[row + 2][col - 2] && cell == boardGrid[row + 3][col - 3]) { @@ -98,16 +100,16 @@ public class Connect4 extends TurnBasedGame { return false; } - public char[][] makeBoardAGrid() { - char[][] boardGrid = new char[rowSize][columnSize]; - for (int i = 0; i < rowSize*columnSize; i++) { - boardGrid[i / columnSize][i % columnSize] = board[i]; //boardGrid[y -> row] [x -> column] + private char[][] makeBoardAGrid() { + char[][] boardGrid = new char[this.getRowSize()][this.getColumnSize()]; + for (int i = 0; i < this.getRowSize()*this.getColumnSize(); i++) { + boardGrid[i / this.getColumnSize()][i % this.getColumnSize()] = this.getBoard()[i]; //this.getBoard()Grid[y -> row] [x -> column] } - return boardGrid; + return boardGrid; } private char getCurrentValue() { - return currentTurn == 0 ? 'X' : 'O'; + return this.getCurrentTurn() == 0 ? 'X' : 'O'; } } diff --git a/game/src/main/java/org/toop/game/Connect4/Connect4AI.java b/game/src/main/java/org/toop/game/Connect4/Connect4AI.java index 420ab0d..45f55d5 100644 --- a/game/src/main/java/org/toop/game/Connect4/Connect4AI.java +++ b/game/src/main/java/org/toop/game/Connect4/Connect4AI.java @@ -1,26 +1,26 @@ package org.toop.game.Connect4; import org.toop.game.AI; -import org.toop.game.Game; -import org.toop.game.tictactoe.TicTacToe; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; public class Connect4AI extends AI { - public Game.Move findBestMove(Connect4 game, int depth) { + public Move findBestMove(Connect4 game, int depth) { assert game != null; assert depth >= 0; - final Game.Move[] legalMoves = game.getLegalMoves(); + final Move[] legalMoves = game.getLegalMoves(); if (legalMoves.length <= 0) { return null; } int bestScore = -depth; - Game.Move bestMove = null; + Move bestMove = null; - for (final Game.Move move : legalMoves) { + for (final Move move : legalMoves) { final int score = getMoveScore(game, depth, move, true); if (score > bestScore) { @@ -32,23 +32,23 @@ public class Connect4AI extends AI { return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; } - private int getMoveScore(Connect4 game, int depth, Game.Move move, boolean maximizing) { + private int getMoveScore(Connect4 game, int depth, Move move, boolean maximizing) { final Connect4 copy = new Connect4(game); - final Game.State state = copy.play(move); + final GameState state = copy.play(move); switch (state) { - case Game.State.DRAW: return 0; - case Game.State.WIN: return maximizing? depth + 1 : -depth - 1; + case GameState.DRAW: return 0; + case GameState.WIN: return maximizing? depth + 1 : -depth - 1; } if (depth <= 0) { return 0; } - final Game.Move[] legalMoves = copy.getLegalMoves(); + final Move[] legalMoves = copy.getLegalMoves(); int score = maximizing? depth + 1 : -depth - 1; - for (final Game.Move next : legalMoves) { + for (final Move next : legalMoves) { if (maximizing) { score = Math.min(score, getMoveScore(copy, depth - 1, next, false)); } else { diff --git a/game/src/main/java/org/toop/game/Game.java b/game/src/main/java/org/toop/game/Game.java index 7b0af68..d0e8b59 100644 --- a/game/src/main/java/org/toop/game/Game.java +++ b/game/src/main/java/org/toop/game/Game.java @@ -1,23 +1,17 @@ package org.toop.game; +import org.toop.game.interfaces.IPlayable; +import org.toop.game.records.Move; + import java.util.Arrays; -public abstract class Game { - public enum State { - NORMAL, - DRAW, - WIN, +public abstract class Game implements IPlayable { - TURN_SKIPPED, - } + public static final char EMPTY = (char)0; // Constant - public record Move(int position, char value) {} - - public static final char EMPTY = (char)0; - - public final int rowSize; - public final int columnSize; - public final char[] board; + private final int rowSize; + private final int columnSize; + private final char[] board; protected Game(int rowSize, int columnSize) { assert rowSize > 0 && columnSize > 0; @@ -35,7 +29,12 @@ public abstract class Game { board = Arrays.copyOf(other.board, other.board.length); } - public abstract Move[] getLegalMoves(); + public int getRowSize() {return this.rowSize;} + + public int getColumnSize() {return this.columnSize;} + + public char[] getBoard() {return this.board;} + + protected void setBoard(Move move){this.board[move.position()] = move.value();} - public abstract State play(Move move); } diff --git a/game/src/main/java/org/toop/game/TurnBasedGame.java b/game/src/main/java/org/toop/game/TurnBasedGame.java index b4eb1d3..4746f56 100644 --- a/game/src/main/java/org/toop/game/TurnBasedGame.java +++ b/game/src/main/java/org/toop/game/TurnBasedGame.java @@ -1,9 +1,8 @@ package org.toop.game; public abstract class TurnBasedGame extends Game { - public final int turns; - - protected int currentTurn; + private final int turns; + private int currentTurn; protected TurnBasedGame(int rowSize, int columnSize, int turns) { super(rowSize, columnSize); diff --git a/game/src/main/java/org/toop/game/enumerators/GameState.java b/game/src/main/java/org/toop/game/enumerators/GameState.java new file mode 100644 index 0000000..4d556e3 --- /dev/null +++ b/game/src/main/java/org/toop/game/enumerators/GameState.java @@ -0,0 +1,9 @@ +package org.toop.game.enumerators; + +public enum GameState { + NORMAL, + DRAW, + WIN, + + TURN_SKIPPED, +} diff --git a/game/src/main/java/org/toop/game/interfaces/IAIMove.java b/game/src/main/java/org/toop/game/interfaces/IAIMove.java new file mode 100644 index 0000000..491f7bb --- /dev/null +++ b/game/src/main/java/org/toop/game/interfaces/IAIMove.java @@ -0,0 +1,8 @@ +package org.toop.game.interfaces; + +import org.toop.game.Game; +import org.toop.game.records.Move; + +public interface IAIMove { + Move findBestMove(T game, int depth); +} diff --git a/game/src/main/java/org/toop/game/interfaces/IPlayable.java b/game/src/main/java/org/toop/game/interfaces/IPlayable.java new file mode 100644 index 0000000..c6ab6c6 --- /dev/null +++ b/game/src/main/java/org/toop/game/interfaces/IPlayable.java @@ -0,0 +1,9 @@ +package org.toop.game.interfaces; + +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; + +public interface IPlayable { + Move[] getLegalMoves(); + GameState play(Move move); +} diff --git a/game/src/main/java/org/toop/game/records/Move.java b/game/src/main/java/org/toop/game/records/Move.java new file mode 100644 index 0000000..3775598 --- /dev/null +++ b/game/src/main/java/org/toop/game/records/Move.java @@ -0,0 +1,3 @@ +package org.toop.game.records; + +public record Move(int position, char value) {} diff --git a/game/src/main/java/org/toop/game/reversi/Reversi.java b/game/src/main/java/org/toop/game/reversi/Reversi.java index b5f503c..a13a44e 100644 --- a/game/src/main/java/org/toop/game/reversi/Reversi.java +++ b/game/src/main/java/org/toop/game/reversi/Reversi.java @@ -1,7 +1,8 @@ package org.toop.game.reversi; -import org.toop.game.Game; import org.toop.game.TurnBasedGame; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import java.awt.*; import java.util.ArrayList; @@ -12,7 +13,6 @@ import java.util.Set; public final class Reversi extends TurnBasedGame { private int movesTaken; - public static final char FIRST_MOVE = 'B'; private Set filledCells = new HashSet<>(); private Move[] mostRecentlyFlippedPieces; @@ -27,20 +27,21 @@ public final class Reversi extends TurnBasedGame { super(other); this.movesTaken = other.movesTaken; this.filledCells = other.filledCells; + this.mostRecentlyFlippedPieces = other.mostRecentlyFlippedPieces; } private void addStartPieces() { - board[27] = 'W'; - board[28] = 'B'; - board[35] = 'B'; - board[36] = 'W'; + this.setBoard(new Move(27, 'W')); + this.setBoard(new Move(28, 'B')); + this.setBoard(new Move(35, 'B')); + this.setBoard(new Move(36, 'W')); updateFilledCellsSet(); } private void updateFilledCellsSet() { for (int i = 0; i < 64; i++) { - if (board[i] == 'W' || board[i] == 'B') { - filledCells.add(new Point(i % columnSize, i / rowSize)); + if (this.getBoard()[i] == 'W' || this.getBoard()[i] == 'B') { + filledCells.add(new Point(i % this.getColumnSize(), i / this.getRowSize())); } } } @@ -49,13 +50,13 @@ public final class Reversi extends TurnBasedGame { public Move[] getLegalMoves() { final ArrayList legalMoves = new ArrayList<>(); char[][] boardGrid = makeBoardAGrid(); - char currentPlayer = (currentTurn==0) ? 'B' : 'W'; + char currentPlayer = (this.getCurrentTurn()==0) ? 'B' : 'W'; Set adjCell = getAdjacentCells(boardGrid); for (Point point : adjCell){ - Move[] moves = getFlipsForPotentialMove(point,boardGrid,currentPlayer); + Move[] moves = getFlipsForPotentialMove(point,currentPlayer); int score = moves.length; if (score > 0){ - legalMoves.add(new Move(point.x + point.y * rowSize, currentPlayer)); + legalMoves.add(new Move(point.x + point.y * this.getRowSize(), currentPlayer)); } } return legalMoves.toArray(new Move[0]); @@ -71,7 +72,7 @@ public final class Reversi extends TurnBasedGame { || !isOnBoard(newX, newY)) { continue; } - if (boardGrid[newY][newX] == Game.EMPTY) { //check if the cell is empty + if (boardGrid[newY][newX] == EMPTY) { //check if the cell is empty possibleCells.add(new Point(newX, newY)); //and then add it to the set of possible moves } } @@ -80,15 +81,15 @@ public final class Reversi extends TurnBasedGame { return possibleCells; } - public Move[] getFlipsForPotentialMove(Point point, char[][] boardGrid, char currentPlayer) { + public Move[] getFlipsForPotentialMove(Point point, char currentPlayer) { final ArrayList movesToFlip = new ArrayList<>(); - for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++) { + for (int deltaColumn = -1; deltaColumn <= 1; deltaColumn++) { //for all directions for (int deltaRow = -1; deltaRow <= 1; deltaRow++) { if (deltaColumn == 0 && deltaRow == 0){ continue; } - Move[] moves = getFlipsInDirection(point,boardGrid,currentPlayer,deltaColumn,deltaRow); - if (moves != null) { + Move[] moves = getFlipsInDirection(point,makeBoardAGrid(),currentPlayer,deltaColumn,deltaRow); + if (moves != null) { //getFlipsInDirection movesToFlip.addAll(Arrays.asList(moves)); } } @@ -102,80 +103,82 @@ public final class Reversi extends TurnBasedGame { int x = point.x + dirX; int y = point.y + dirY; - if (!isOnBoard(x, y) || boardGrid[y][x] != opponent) { + if (!isOnBoard(x, y) || boardGrid[y][x] != opponent) { //there must first be an opponents tile return null; } - while (isOnBoard(x, y) && boardGrid[y][x] == opponent) { + while (isOnBoard(x, y) && boardGrid[y][x] == opponent) { //count the opponents tiles in this direction - movesToFlip.add(new Move(x+y*rowSize, currentPlayer)); + movesToFlip.add(new Move(x+y*this.getRowSize(), currentPlayer)); x += dirX; y += dirY; } if (isOnBoard(x, y) && boardGrid[y][x] == currentPlayer) { - return movesToFlip.toArray(new Move[0]); + return movesToFlip.toArray(new Move[0]); //only return the count if last tile is ours } return null; } private boolean isOnBoard(int x, int y) { - return x >= 0 && x < columnSize && y >= 0 && y < rowSize; + return x >= 0 && x < this.getColumnSize() && y >= 0 && y < this.getRowSize(); } - public char[][] makeBoardAGrid() { - char[][] boardGrid = new char[rowSize][columnSize]; + private char[][] makeBoardAGrid() { + char[][] boardGrid = new char[this.getRowSize()][this.getColumnSize()]; for (int i = 0; i < 64; i++) { - boardGrid[i / rowSize][i % columnSize] = board[i]; //boardGrid[y / row] [x / column] + boardGrid[i / this.getRowSize()][i % this.getColumnSize()] = this.getBoard()[i]; //boardGrid[y -> row] [x -> column] } return boardGrid; } + @Override - public State play(Move move) { + public GameState play(Move move) { Move[] legalMoves = getLegalMoves(); boolean moveIsLegal = false; - for (Move legalMove : legalMoves) { + for (Move legalMove : legalMoves) { //check if the move is legal if (move.equals(legalMove)) { moveIsLegal = true; break; } } - if (moveIsLegal) { - Move[] moves = sortMovesFromCenter(getFlipsForPotentialMove(new Point(move.position()%columnSize,move.position()/rowSize), makeBoardAGrid(), move.value()),move); - mostRecentlyFlippedPieces = moves; - board[move.position()] = move.value(); - for (Move m : moves) { - board[m.position()] = m.value(); + if (!moveIsLegal) { + return null; + } + + Move[] moves = sortMovesFromCenter(getFlipsForPotentialMove(new Point(move.position()%this.getColumnSize(),move.position()/this.getRowSize()), move.value()),move); + mostRecentlyFlippedPieces = moves; + this.setBoard(move); //place the move on the board + for (Move m : moves) { + this.setBoard(m); //flip the correct pieces on the board + } + filledCells.add(new Point(move.position() % this.getRowSize(), move.position() / this.getColumnSize())); + nextTurn(); + if (getLegalMoves().length == 0) { //skip the players turn when there are no legal moves + skipMyTurn(); + if (getLegalMoves().length > 0) { + return GameState.TURN_SKIPPED; } - filledCells.add(new Point(move.position() % rowSize, move.position() / columnSize)); - nextTurn(); - if (getLegalMoves().length == 0) { - skipMyTurn(); - if (getLegalMoves().length > 0) { - return State.TURN_SKIPPED; + else { //end the game when neither player has a legal move + Score score = getScore(); + if (score.player1Score() == score.player2Score()) { + return GameState.DRAW; } else { - Score score = getScore(); - if (score.player1Score() == score.player2Score()) { - return State.DRAW; - } - else { - return State.WIN; - } + return GameState.WIN; } } - return State.NORMAL; } - return null; + return GameState.NORMAL; } - public void skipMyTurn(){ + private void skipMyTurn(){ IO.println("TURN " + getCurrentPlayer() + " SKIPPED"); - //todo notify user that a turn has been skipped + //TODO: notify user that a turn has been skipped nextTurn(); } public char getCurrentPlayer() { - if (currentTurn == 0){ + if (this.getCurrentTurn() == 0){ return 'B'; } else { @@ -194,24 +197,24 @@ public final class Reversi extends TurnBasedGame { public Score getScore(){ int player1Score = 0, player2Score = 0; - for (int count = 0; count < rowSize * columnSize; count++) { - if (board[count] == 'B') { + for (int count = 0; count < this.getRowSize() * this.getColumnSize(); count++) { + if (this.getBoard()[count] == 'B') { player1Score += 1; } - if (board[count] == 'W') { + if (this.getBoard()[count] == 'W') { player2Score += 1; } } return new Score(player1Score, player2Score); } - public Move[] sortMovesFromCenter(Move[] moves, Move center) { - int centerX = center.position()%columnSize; - int centerY = center.position()/rowSize; + private Move[] sortMovesFromCenter(Move[] moves, Move center) { //sorts the pieces to be flipped for animation purposes + int centerX = center.position()%this.getColumnSize(); + int centerY = center.position()/this.getRowSize(); Arrays.sort(moves, (a, b) -> { - int dxA = a.position()%columnSize - centerX; - int dyA = a.position()/rowSize - centerY; - int dxB = b.position()%columnSize - centerX; - int dyB = b.position()/rowSize - centerY; + int dxA = a.position()%this.getColumnSize() - centerX; + int dyA = a.position()/this.getRowSize() - centerY; + int dxB = b.position()%this.getColumnSize() - centerX; + int dyB = b.position()/this.getRowSize() - centerY; int distA = dxA * dxA + dyA * dyA; int distB = dxB * dxB + dyB * dyB; diff --git a/game/src/main/java/org/toop/game/reversi/ReversiAI.java b/game/src/main/java/org/toop/game/reversi/ReversiAI.java index a532a62..2fc78d5 100644 --- a/game/src/main/java/org/toop/game/reversi/ReversiAI.java +++ b/game/src/main/java/org/toop/game/reversi/ReversiAI.java @@ -1,13 +1,14 @@ package org.toop.game.reversi; import org.toop.game.AI; -import org.toop.game.Game; +import org.toop.game.records.Move; public final class ReversiAI extends AI { @Override - public Game.Move findBestMove(Reversi game, int depth) { - Game.Move[] moves = game.getLegalMoves(); + public Move findBestMove(Reversi game, int depth) { + Move[] moves = game.getLegalMoves(); int inty = (int)(Math.random() * moves.length-.5f); + if (moves.length == 0) return null; return moves[inty]; } } diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java index 0fa6ca8..3d8f5f7 100644 --- a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java +++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java @@ -2,13 +2,15 @@ package org.toop.game.tictactoe; import java.util.ArrayList; import org.toop.game.TurnBasedGame; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; public final class TicTacToe extends TurnBasedGame { private int movesLeft; public TicTacToe() { super(3, 3, 2); - movesLeft = board.length; + movesLeft = this.getBoard().length; } public TicTacToe(TicTacToe other) { @@ -21,8 +23,8 @@ public final class TicTacToe extends TurnBasedGame { final ArrayList legalMoves = new ArrayList<>(); final char currentValue = getCurrentValue(); - for (int i = 0; i < board.length; i++) { - if (board[i] == EMPTY) { + for (int i = 0; i < this.getBoard().length; i++) { + if (this.getBoard()[i] == EMPTY) { legalMoves.add(new Move(i, currentValue)); } } @@ -31,27 +33,28 @@ public final class TicTacToe extends TurnBasedGame { } @Override - public State play(Move move) { + public GameState play(Move move) { assert move != null; - assert move.position() >= 0 && move.position() < board.length; + assert move.position() >= 0 && move.position() < this.getBoard().length; assert move.value() == getCurrentValue(); - board[move.position()] = move.value(); + // TODO: Make sure this move is allowed, maybe on the board side? + this.setBoard(move); movesLeft--; if (checkForWin()) { - return State.WIN; + return GameState.WIN; } nextTurn(); if (movesLeft <= 2) { - if (movesLeft <= 0 || checkForEarlyDraw(this)) { - return State.DRAW; + if (movesLeft <= 0 || checkForEarlyDraw()) { + return GameState.DRAW; } } - return State.NORMAL; + return GameState.NORMAL; } private boolean checkForWin() { @@ -59,34 +62,34 @@ public final class TicTacToe extends TurnBasedGame { for (int i = 0; i < 3; i++) { final int index = i * 3; - if (board[index] != EMPTY - && board[index] == board[index + 1] - && board[index] == board[index + 2]) { + if (this.getBoard()[index] != EMPTY + && this.getBoard()[index] == this.getBoard()[index + 1] + && this.getBoard()[index] == this.getBoard()[index + 2]) { return true; } } // Vertical for (int i = 0; i < 3; i++) { - if (board[i] != EMPTY && board[i] == board[i + 3] && board[i] == board[i + 6]) { + if (this.getBoard()[i] != EMPTY && this.getBoard()[i] == this.getBoard()[i + 3] && this.getBoard()[i] == this.getBoard()[i + 6]) { return true; } } // B-Slash - if (board[0] != EMPTY && board[0] == board[4] && board[0] == board[8]) { + if (this.getBoard()[0] != EMPTY && this.getBoard()[0] == this.getBoard()[4] && this.getBoard()[0] == this.getBoard()[8]) { return true; } // F-Slash - return board[2] != EMPTY && board[2] == board[4] && board[2] == board[6]; + return this.getBoard()[2] != EMPTY && this.getBoard()[2] == this.getBoard()[4] && this.getBoard()[2] == this.getBoard()[6]; } - private boolean checkForEarlyDraw(TicTacToe game) { - for (final Move move : game.getLegalMoves()) { - final TicTacToe copy = new TicTacToe(game); + private boolean checkForEarlyDraw() { + for (final Move move : this.getLegalMoves()) { + final TicTacToe copy = new TicTacToe(this); - if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) { + if (copy.play(move) == GameState.WIN || !copy.checkForEarlyDraw()) { return false; } } @@ -95,6 +98,6 @@ public final class TicTacToe extends TurnBasedGame { } private char getCurrentValue() { - return currentTurn == 0 ? 'X' : 'O'; + return this.getCurrentTurn() == 0 ? 'X' : 'O'; } } diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java index 59341e5..3f10ab4 100644 --- a/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java +++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java @@ -1,15 +1,16 @@ package org.toop.game.tictactoe; import org.toop.game.AI; -import org.toop.game.Game; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; public final class TicTacToeAI extends AI { @Override - public Game.Move findBestMove(TicTacToe game, int depth) { + public Move findBestMove(TicTacToe game, int depth) { assert game != null; assert depth >= 0; - final Game.Move[] legalMoves = game.getLegalMoves(); + final Move[] legalMoves = game.getLegalMoves(); if (legalMoves.length == 0) { return null; @@ -25,9 +26,9 @@ public final class TicTacToeAI extends AI { } int bestScore = -depth; - Game.Move bestMove = null; + Move bestMove = null; - for (final Game.Move move : legalMoves) { + for (final Move move : legalMoves) { final int score = getMoveScore(game, depth, move, true); if (score > bestScore) { @@ -38,15 +39,15 @@ public final class TicTacToeAI extends AI { return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; } - public Game.Move findWorstMove(TicTacToe game, int depth){ + public Move findWorstMove(TicTacToe game, int depth){ - Game.Move[] legalMoves = game.getLegalMoves(); + Move[] legalMoves = game.getLegalMoves(); int bestScore = -depth; - Game.Move bestMove = null; + Move bestMove = null; - for (final Game.Move move : legalMoves) { + for (final Move move : legalMoves) { final int score = getMoveScore(game, depth, move, false); if (score > bestScore) { @@ -57,23 +58,23 @@ public final class TicTacToeAI extends AI { return bestMove; } - private int getMoveScore(TicTacToe game, int depth, Game.Move move, boolean maximizing) { + private int getMoveScore(TicTacToe game, int depth, Move move, boolean maximizing) { final TicTacToe copy = new TicTacToe(game); - final Game.State state = copy.play(move); + final GameState state = copy.play(move); switch (state) { - case Game.State.DRAW: return 0; - case Game.State.WIN: return maximizing? depth + 1 : -depth - 1; + case GameState.DRAW: return 0; + case GameState.WIN: return maximizing? depth + 1 : -depth - 1; } if (depth <= 0) { return 0; } - final Game.Move[] legalMoves = copy.getLegalMoves(); + final Move[] legalMoves = copy.getLegalMoves(); int score = maximizing? depth + 1 : -depth - 1; - for (final Game.Move next : legalMoves) { + for (final Move next : legalMoves) { if (maximizing) { score = Math.min(score, getMoveScore(copy, depth - 1, next, false)); } else { diff --git a/game/src/test/java/org/toop/game/tictactoe/ReversiTest.java b/game/src/test/java/org/toop/game/tictactoe/ReversiTest.java index fa3b897..f2586a5 100644 --- a/game/src/test/java/org/toop/game/tictactoe/ReversiTest.java +++ b/game/src/test/java/org/toop/game/tictactoe/ReversiTest.java @@ -1,11 +1,11 @@ package org.toop.game.tictactoe; -import org.toop.game.Game; - import java.util.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.toop.game.enumerators.GameState; +import org.toop.game.records.Move; import org.toop.game.reversi.Reversi; import org.toop.game.reversi.ReversiAI; @@ -25,27 +25,27 @@ class ReversiTest { @Test void testCorrectStartPiecesPlaced() { assertNotNull(game); - assertEquals('W',game.board[27]); - assertEquals('B',game.board[28]); - assertEquals('B',game.board[35]); - assertEquals('W',game.board[36]); + assertEquals('W',game.getBoard()[27]); + assertEquals('B',game.getBoard()[28]); + assertEquals('B',game.getBoard()[35]); + assertEquals('W',game.getBoard()[36]); } @Test void testGetLegalMovesAtStart() { - Game.Move[] moves = game.getLegalMoves(); - List expectedMoves = List.of( - new Game.Move(19,'B'), - new Game.Move(26,'B'), - new Game.Move(37,'B'), - new Game.Move(44,'B') + Move[] moves = game.getLegalMoves(); + List expectedMoves = List.of( + new Move(19,'B'), + new Move(26,'B'), + new Move(37,'B'), + new Move(44,'B') ); assertNotNull(moves); assertTrue(moves.length > 0); assertMovesMatchIgnoreOrder(expectedMoves, Arrays.asList(moves)); } - private void assertMovesMatchIgnoreOrder(List expected, List actual) { + private void assertMovesMatchIgnoreOrder(List expected, List actual) { assertEquals(expected.size(), actual.size()); for (int i = 0; i < expected.size(); i++) { assertTrue(actual.contains(expected.get(i))); @@ -55,16 +55,16 @@ class ReversiTest { @Test void testMakeValidMoveFlipsPieces() { - game.play(new Game.Move(19, 'B')); - assertEquals('B', game.board[19]); - assertEquals('B', game.board[27], "Piece should have flipped to B"); + game.play(new Move(19, 'B')); + assertEquals('B', game.getBoard()[19]); + assertEquals('B', game.getBoard()[27], "Piece should have flipped to B"); } @Test void testMakeInvalidMoveDoesNothing() { - char[] before = game.board.clone(); - game.play(new Game.Move(0, 'B')); - assertArrayEquals(before, game.board, "Board should not change on invalid move"); + char[] before = game.getBoard().clone(); + game.play(new Move(0, 'B')); + assertArrayEquals(before, game.getBoard(), "Board should not change on invalid move"); } @Test @@ -77,7 +77,7 @@ class ReversiTest { @Test void testCountScoreCorrectlyAtStart() { long start = System.nanoTime(); - Game.Score score = game.getScore(); + Reversi.Score score = game.getScore(); assertEquals(2, score.player1Score()); // Black assertEquals(2, score.player2Score()); // White long end = System.nanoTime(); @@ -86,15 +86,15 @@ class ReversiTest { @Test void zLegalMovesInCertainPosition() { - game.play(new Game.Move(19, 'B')); - game.play(new Game.Move(20, 'W')); - Game.Move[] moves = game.getLegalMoves(); - List expectedMoves = List.of( - new Game.Move(13,'B'), - new Game.Move(21, 'B'), - new Game.Move(29, 'B'), - new Game.Move(37, 'B'), - new Game.Move(45, 'B')); + game.play(new Move(19, 'B')); + game.play(new Move(20, 'W')); + Move[] moves = game.getLegalMoves(); + List expectedMoves = List.of( + new Move(13,'B'), + new Move(21, 'B'), + new Move(29, 'B'), + new Move(37, 'B'), + new Move(45, 'B')); assertNotNull(moves); assertTrue(moves.length > 0); IO.println(Arrays.toString(moves)); @@ -105,45 +105,45 @@ class ReversiTest { void testCountScoreCorrectlyAtEnd() { for (int i = 0; i < 1; i++){ game = new Reversi(); - Game.Move[] legalMoves = game.getLegalMoves(); + Move[] legalMoves = game.getLegalMoves(); while(legalMoves.length > 0) { game.play(legalMoves[(int)(Math.random()*legalMoves.length)]); legalMoves = game.getLegalMoves(); } - Game.Score score = game.getScore(); + Reversi.Score score = game.getScore(); IO.println(score.player1Score()); IO.println(score.player2Score()); - char[][] grid = game.makeBoardAGrid(); - for (char[] chars : grid) { - IO.println(Arrays.toString(chars)); - } + for (int r = 0; r < game.getRowSize(); r++) { + char[] row = Arrays.copyOfRange(game.getBoard(), r * game.getColumnSize(), (r + 1) * game.getColumnSize()); + IO.println(Arrays.toString(row)); + } } } @Test void testPlayerMustSkipTurnIfNoValidMoves() { - game.play(new Game.Move(19, 'B')); - game.play(new Game.Move(34, 'W')); - game.play(new Game.Move(45, 'B')); - game.play(new Game.Move(11, 'W')); - game.play(new Game.Move(42, 'B')); - game.play(new Game.Move(54, 'W')); - game.play(new Game.Move(37, 'B')); - game.play(new Game.Move(46, 'W')); - game.play(new Game.Move(63, 'B')); - game.play(new Game.Move(62, 'W')); - game.play(new Game.Move(29, 'B')); - game.play(new Game.Move(50, 'W')); - game.play(new Game.Move(55, 'B')); - game.play(new Game.Move(30, 'W')); - game.play(new Game.Move(53, 'B')); - game.play(new Game.Move(38, 'W')); - game.play(new Game.Move(61, 'B')); - game.play(new Game.Move(52, 'W')); - game.play(new Game.Move(51, 'B')); - game.play(new Game.Move(60, 'W')); - game.play(new Game.Move(59, 'B')); + game.play(new Move(19, 'B')); + game.play(new Move(34, 'W')); + game.play(new Move(45, 'B')); + game.play(new Move(11, 'W')); + game.play(new Move(42, 'B')); + game.play(new Move(54, 'W')); + game.play(new Move(37, 'B')); + game.play(new Move(46, 'W')); + game.play(new Move(63, 'B')); + game.play(new Move(62, 'W')); + game.play(new Move(29, 'B')); + game.play(new Move(50, 'W')); + game.play(new Move(55, 'B')); + game.play(new Move(30, 'W')); + game.play(new Move(53, 'B')); + game.play(new Move(38, 'W')); + game.play(new Move(61, 'B')); + game.play(new Move(52, 'W')); + game.play(new Move(51, 'B')); + game.play(new Move(60, 'W')); + game.play(new Move(59, 'B')); assertEquals('B', game.getCurrentPlayer()); game.play(ai.findBestMove(game,5)); game.play(ai.findBestMove(game,5)); @@ -152,40 +152,40 @@ class ReversiTest { @Test void testGameShouldEndIfNoValidMoves() { //European Grand Prix Ghent 2017: Replay Hassan - Verstuyft J. (3-17) - game.play(new Game.Move(19, 'B')); - game.play(new Game.Move(20, 'W')); - game.play(new Game.Move(29, 'B')); - game.play(new Game.Move(22, 'W')); - game.play(new Game.Move(21, 'B')); - game.play(new Game.Move(34, 'W')); - game.play(new Game.Move(23, 'B')); - game.play(new Game.Move(13, 'W')); - game.play(new Game.Move(26, 'B')); - game.play(new Game.Move(18, 'W')); - game.play(new Game.Move(12, 'B')); - game.play(new Game.Move(4, 'W')); - game.play(new Game.Move(17, 'B')); - game.play(new Game.Move(31, 'W')); - Game.State stateTurn15 = game.play(new Game.Move(39, 'B')); - assertEquals(Game.State.NORMAL, stateTurn15); - Game.State stateTurn16 = game.play(new Game.Move(16, 'W')); - assertEquals(Game.State.WIN, stateTurn16); - Game.State stateTurn17 = game.play(new Game.Move(5, 'B')); + game.play(new Move(19, 'B')); + game.play(new Move(20, 'W')); + game.play(new Move(29, 'B')); + game.play(new Move(22, 'W')); + game.play(new Move(21, 'B')); + game.play(new Move(34, 'W')); + game.play(new Move(23, 'B')); + game.play(new Move(13, 'W')); + game.play(new Move(26, 'B')); + game.play(new Move(18, 'W')); + game.play(new Move(12, 'B')); + game.play(new Move(4, 'W')); + game.play(new Move(17, 'B')); + game.play(new Move(31, 'W')); + GameState stateTurn15 = game.play(new Move(39, 'B')); + assertEquals(GameState.NORMAL, stateTurn15); + GameState stateTurn16 = game.play(new Move(16, 'W')); + assertEquals(GameState.WIN, stateTurn16); + GameState stateTurn17 = game.play(new Move(5, 'B')); assertNull(stateTurn17); - Game.Score score = game.getScore(); + Reversi.Score score = game.getScore(); assertEquals(3, score.player1Score()); assertEquals(17, score.player2Score()); } @Test void testAISelectsLegalMove() { - Game.Move move = ai.findBestMove(game,4); + Move move = ai.findBestMove(game,4); assertNotNull(move); assertTrue(containsMove(game.getLegalMoves(),move), "AI should always choose a legal move"); } - private boolean containsMove(Game.Move[] moves, Game.Move move) { - for (Game.Move m : moves) { + private boolean containsMove(Move[] moves, Move move) { + for (Move m : moves) { if (m.equals(move)) return true; } return false; diff --git a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java index 325b8ee..c1c0d85 100644 --- a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java +++ b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.toop.game.Game; +import org.toop.game.records.Move; class TicTacToeAITest { private TicTacToe game; @@ -22,12 +22,12 @@ class TicTacToeAITest { // X X - // O O - // - - - - game.play(new Game.Move(0, 'X')); - game.play(new Game.Move(3, 'O')); - game.play(new Game.Move(1, 'X')); - game.play(new Game.Move(4, 'O')); + game.play(new Move(0, 'X')); + game.play(new Move(3, 'O')); + game.play(new Move(1, 'X')); + game.play(new Move(4, 'O')); - final Game.Move move = ai.findBestMove(game, 1); + final Move move = ai.findBestMove(game, 1); assertNotNull(move); assertEquals('X', move.value()); @@ -39,11 +39,11 @@ class TicTacToeAITest { // - - - // O - - // X X - - game.play(new Game.Move(6, 'X')); - game.play(new Game.Move(3, 'O')); - game.play(new Game.Move(7, 'X')); + game.play(new Move(6, 'X')); + game.play(new Move(3, 'O')); + game.play(new Move(7, 'X')); - final Game.Move move = ai.findBestMove(game, 1); + final Move move = ai.findBestMove(game, 1); assertNotNull(move); assertEquals('O', move.value()); @@ -52,7 +52,7 @@ class TicTacToeAITest { @Test void testBestMove_preferCornerOnEmpty() { - final Game.Move move = ai.findBestMove(game, 0); + final Move move = ai.findBestMove(game, 0); assertNotNull(move); assertEquals('X', move.value()); @@ -64,15 +64,15 @@ class TicTacToeAITest { // O X - // - O X // X O X - game.play(new Game.Move(1, 'X')); - game.play(new Game.Move(0, 'O')); - game.play(new Game.Move(5, 'X')); - game.play(new Game.Move(4, 'O')); - game.play(new Game.Move(6, 'X')); - game.play(new Game.Move(7, 'O')); - game.play(new Game.Move(8, 'X')); + game.play(new Move(1, 'X')); + game.play(new Move(0, 'O')); + game.play(new Move(5, 'X')); + game.play(new Move(4, 'O')); + game.play(new Move(6, 'X')); + game.play(new Move(7, 'O')); + game.play(new Move(8, 'X')); - final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length); + final Move move = ai.findBestMove(game, game.getLegalMoves().length); assertNotNull(move); assertEquals('O', move.value());