mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
add simple flip animations and fixed(?) server somewhat
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package org.toop.app;
|
package org.toop.app;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.AbstractScheduledService;
|
|
||||||
import org.toop.app.game.Connect4Game;
|
import org.toop.app.game.Connect4Game;
|
||||||
import org.toop.app.game.ReversiGame;
|
import org.toop.app.game.ReversiGame;
|
||||||
import org.toop.app.game.TicTacToeGame;
|
import org.toop.app.game.TicTacToeGame;
|
||||||
@@ -13,12 +12,9 @@ import org.toop.app.view.views.ServerView;
|
|||||||
import org.toop.framework.eventbus.EventFlow;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
import org.toop.framework.networking.clients.TournamentNetworkingClient;
|
import org.toop.framework.networking.clients.TournamentNetworkingClient;
|
||||||
import org.toop.framework.networking.events.NetworkEvents;
|
import org.toop.framework.networking.events.NetworkEvents;
|
||||||
import org.toop.framework.networking.interfaces.NetworkingClient;
|
|
||||||
import org.toop.framework.networking.types.NetworkingConnector;
|
import org.toop.framework.networking.types.NetworkingConnector;
|
||||||
import org.toop.local.AppContext;
|
import org.toop.local.AppContext;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -27,25 +23,26 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
public final class Server {
|
public final class Server {
|
||||||
private String user = "";
|
private String user = "";
|
||||||
|
|
||||||
private long clientId = -1;
|
private long clientId = -1;
|
||||||
private List<String> onlinePlayers = new CopyOnWriteArrayList<String>();
|
|
||||||
private List<String> gameList = new CopyOnWriteArrayList<>();
|
private final List<String> onlinePlayers = new CopyOnWriteArrayList<>();
|
||||||
|
private final List<String> gameList = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private ServerView view;
|
private ServerView view;
|
||||||
|
|
||||||
private boolean isPolling = true;
|
private boolean isPolling = true;
|
||||||
|
|
||||||
|
private ScheduledExecutorService scheduler;
|
||||||
|
|
||||||
public static GameInformation.Type gameToType(String game) {
|
public static GameInformation.Type gameToType(String game) {
|
||||||
if (game.equalsIgnoreCase("tic-tac-toe")) {
|
if (game.equalsIgnoreCase("tic-tac-toe")) {
|
||||||
return GameInformation.Type.TICTACTOE;
|
return GameInformation.Type.TICTACTOE;
|
||||||
} else if (game.equalsIgnoreCase("reversi")) {
|
} else if (game.equalsIgnoreCase("reversi")) {
|
||||||
return GameInformation.Type.REVERSI;
|
return GameInformation.Type.REVERSI;
|
||||||
} else if (game.equalsIgnoreCase("connect4")) {
|
} else if (game.equalsIgnoreCase("connect4")) {
|
||||||
return GameInformation.Type.CONNECT4;
|
return GameInformation.Type.CONNECT4;
|
||||||
} else if (game.equalsIgnoreCase("battleship")) {
|
} else if (game.equalsIgnoreCase("battleship")) {
|
||||||
return GameInformation.Type.BATTLESHIP;
|
return GameInformation.Type.BATTLESHIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -56,7 +53,7 @@ public final class Server {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parsedPort = -1;
|
int parsedPort;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parsedPort = Integer.parseInt(port);
|
parsedPort = Integer.parseInt(port);
|
||||||
@@ -72,11 +69,10 @@ public final class Server {
|
|||||||
|
|
||||||
new EventFlow()
|
new EventFlow()
|
||||||
.addPostEvent(NetworkEvents.StartClient.class,
|
.addPostEvent(NetworkEvents.StartClient.class,
|
||||||
new TournamentNetworkingClient(),
|
new TournamentNetworkingClient(),
|
||||||
new NetworkingConnector(ip, parsedPort, 10, 1, TimeUnit.SECONDS)
|
new NetworkingConnector(ip, parsedPort, 10, 1, TimeUnit.SECONDS)
|
||||||
)
|
)
|
||||||
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
.onResponse(NetworkEvents.StartClientResponse.class, e -> {
|
||||||
// TODO add if unsuccessful
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
clientId = e.clientId();
|
clientId = e.clientId();
|
||||||
|
|
||||||
@@ -86,29 +82,15 @@ public final class Server {
|
|||||||
ViewStack.push(view);
|
ViewStack.push(view);
|
||||||
|
|
||||||
startPopulateScheduler();
|
startPopulateScheduler();
|
||||||
|
populateGameList();
|
||||||
populateGameList();
|
|
||||||
|
|
||||||
}).postEvent();
|
}).postEvent();
|
||||||
|
|
||||||
new EventFlow().listen(this::handleReceivedChallenge);
|
new EventFlow().listen(this::handleReceivedChallenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populatePlayerList(ScheduledExecutorService scheduler, Runnable populatingTask) {
|
|
||||||
|
|
||||||
final long DELAY = 5;
|
|
||||||
|
|
||||||
if (!isPolling) scheduler.shutdown();
|
|
||||||
else {
|
|
||||||
populatingTask.run();
|
|
||||||
scheduler.schedule(() -> populatePlayerList(scheduler, populatingTask), DELAY, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendChallenge(String opponent) {
|
private void sendChallenge(String opponent) {
|
||||||
if (!isPolling) {
|
if (!isPolling) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> {
|
ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> {
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType))
|
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType))
|
||||||
@@ -118,7 +100,12 @@ public final class Server {
|
|||||||
onlinePlayers.clear();
|
onlinePlayers.clear();
|
||||||
|
|
||||||
final GameInformation.Type type = gameToType(gameType);
|
final GameInformation.Type type = gameToType(gameType);
|
||||||
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent())? 1 : 0;
|
if (type == null) {
|
||||||
|
ViewStack.push(new ErrorView("Unsupported game type: " + gameType));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent()) ? 1 : 0;
|
||||||
|
|
||||||
final GameInformation information = new GameInformation(type);
|
final GameInformation information = new GameInformation(type);
|
||||||
information.players[0] = playerInformation;
|
information.players[0] = playerInformation;
|
||||||
@@ -126,9 +113,10 @@ public final class Server {
|
|||||||
information.players[1].name = opponent;
|
information.players[1].name = opponent;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TICTACTOE: new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case TICTACTOE -> new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
case REVERSI: new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case REVERSI -> new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
case CONNECT4: new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case CONNECT4 -> new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
|
default -> ViewStack.push(new ErrorView("Unsupported game type."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).postEvent();
|
}).postEvent();
|
||||||
@@ -136,22 +124,14 @@ public final class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
|
||||||
if (!isPolling) {
|
if (!isPolling) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String challengerName = response.challengerName();
|
|
||||||
challengerName = challengerName.substring(challengerName.indexOf("\"") + 1);
|
|
||||||
challengerName = challengerName.substring(0, challengerName.indexOf("\""));
|
|
||||||
|
|
||||||
String gameType = response.gameType();
|
|
||||||
gameType = gameType.substring(gameType.indexOf("\"") + 1);
|
|
||||||
gameType = gameType.substring(0, gameType.indexOf("\""));
|
|
||||||
|
|
||||||
|
String challengerName = extractQuotedValue(response.challengerName());
|
||||||
|
String gameType = extractQuotedValue(response.gameType());
|
||||||
final String finalGameType = gameType;
|
final String finalGameType = gameType;
|
||||||
|
|
||||||
ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> {
|
ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> {
|
||||||
final int challengeId = Integer.parseInt(response.challengeId().substring(18, response.challengeId().length() - 2));
|
final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", ""));
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent();
|
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent();
|
||||||
|
|
||||||
ViewStack.pop();
|
ViewStack.pop();
|
||||||
@@ -162,7 +142,12 @@ public final class Server {
|
|||||||
onlinePlayers.clear();
|
onlinePlayers.clear();
|
||||||
|
|
||||||
final GameInformation.Type type = gameToType(finalGameType);
|
final GameInformation.Type type = gameToType(finalGameType);
|
||||||
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent())? 1 : 0;
|
if (type == null) {
|
||||||
|
ViewStack.push(new ErrorView("Unsupported game type: " + finalGameType));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int myTurn = e.playerToMove().equalsIgnoreCase(e.opponent()) ? 1 : 0;
|
||||||
|
|
||||||
final GameInformation information = new GameInformation(type);
|
final GameInformation information = new GameInformation(type);
|
||||||
information.players[0] = playerInformation;
|
information.players[0] = playerInformation;
|
||||||
@@ -170,9 +155,10 @@ public final class Server {
|
|||||||
information.players[1].name = e.opponent();
|
information.players[1].name = e.opponent();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TICTACTOE: new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case TICTACTOE -> new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
case REVERSI: new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case REVERSI -> new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
case CONNECT4: new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); break;
|
case CONNECT4 -> new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage);
|
||||||
|
default -> ViewStack.push(new ErrorView("Unsupported game type."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -186,6 +172,7 @@ public final class Server {
|
|||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent();
|
new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent();
|
||||||
isPolling = false;
|
isPolling = false;
|
||||||
|
stopScheduler();
|
||||||
ViewStack.push(new OnlineView());
|
ViewStack.push(new OnlineView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,41 +182,61 @@ public final class Server {
|
|||||||
|
|
||||||
private void exitGame() {
|
private void exitGame() {
|
||||||
forfeitGame();
|
forfeitGame();
|
||||||
|
|
||||||
ViewStack.push(view);
|
ViewStack.push(view);
|
||||||
startPopulateScheduler();
|
startPopulateScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPopulateScheduler() {
|
private void startPopulateScheduler() {
|
||||||
isPolling = true;
|
isPolling = true;
|
||||||
|
stopScheduler();
|
||||||
|
|
||||||
EventFlow getPlayerlistFlow = new EventFlow()
|
new EventFlow()
|
||||||
.addPostEvent(new NetworkEvents.SendGetPlayerlist(clientId))
|
|
||||||
.listen(NetworkEvents.PlayerlistResponse.class, e -> {
|
.listen(NetworkEvents.PlayerlistResponse.class, e -> {
|
||||||
if (e.clientId() == clientId) {
|
if (e.clientId() == clientId) {
|
||||||
onlinePlayers = new ArrayList<>(List.of(e.playerlist()));
|
onlinePlayers.clear();
|
||||||
onlinePlayers.removeIf(name -> name.equalsIgnoreCase(user));
|
onlinePlayers.addAll(List.of(e.playerlist()));
|
||||||
|
onlinePlayers.removeIf(name -> name.equalsIgnoreCase(user));
|
||||||
|
view.update(onlinePlayers);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
view.update(onlinePlayers);
|
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
scheduler.scheduleAtFixedRate(() -> {
|
||||||
|
if (isPolling) {
|
||||||
|
new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(clientId)).postEvent();
|
||||||
|
} else {
|
||||||
|
stopScheduler();
|
||||||
}
|
}
|
||||||
}, false);
|
}, 0, 5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
scheduler.schedule(() -> populatePlayerList(scheduler, getPlayerlistFlow::postEvent), 0, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gamesListFromServerHandler(NetworkEvents.GamelistResponse event) {
|
private void stopScheduler() {
|
||||||
gameList.addAll(List.of(event.gamelist()));
|
if (scheduler != null && !scheduler.isShutdown()) {
|
||||||
}
|
scheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void gamesListFromServerHandler(NetworkEvents.GamelistResponse event) {
|
||||||
|
gameList.clear();
|
||||||
|
gameList.addAll(List.of(event.gamelist()));
|
||||||
|
}
|
||||||
|
|
||||||
public void populateGameList() {
|
public void populateGameList() {
|
||||||
new EventFlow().addPostEvent(new NetworkEvents.SendGetGamelist(clientId))
|
new EventFlow().addPostEvent(new NetworkEvents.SendGetGamelist(clientId))
|
||||||
.listen(NetworkEvents.GamelistResponse.class,
|
.listen(NetworkEvents.GamelistResponse.class, this::gamesListFromServerHandler, true)
|
||||||
this::gamesListFromServerHandler, true
|
.postEvent();
|
||||||
).postEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getGameList() {
|
public List<String> getGameList() {
|
||||||
return gameList;
|
return gameList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractQuotedValue(String s) {
|
||||||
|
int first = s.indexOf('"');
|
||||||
|
int last = s.lastIndexOf('"');
|
||||||
|
if (first >= 0 && last > first) {
|
||||||
|
return s.substring(first + 1, last);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,6 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
public class Connect4Canvas extends GameCanvas {
|
public class Connect4Canvas extends GameCanvas {
|
||||||
public Connect4Canvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
public Connect4Canvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
|
||||||
super(color, width, height, 6, 7, 10, true, onCellClicked);
|
super(color, width, height, 7, 6, 10, true, onCellClicked);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package org.toop.app.canvas;
|
package org.toop.app.canvas;
|
||||||
|
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
import javafx.scene.canvas.Canvas;
|
import javafx.scene.canvas.Canvas;
|
||||||
import javafx.scene.canvas.GraphicsContext;
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
import javafx.scene.input.MouseButton;
|
import javafx.scene.input.MouseButton;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public abstract class GameCanvas {
|
public abstract class GameCanvas {
|
||||||
@@ -49,54 +51,46 @@ public abstract class GameCanvas {
|
|||||||
|
|
||||||
cells = new Cell[rowSize * columnSize];
|
cells = new Cell[rowSize * columnSize];
|
||||||
|
|
||||||
final float cellWidth = ((float)width - gapSize * columnSize - gapSize) / columnSize;
|
final float cellWidth = ((float) width - gapSize * rowSize - gapSize) / rowSize;
|
||||||
final float cellHeight = ((float)height - gapSize * rowSize - gapSize) / rowSize;
|
final float cellHeight = ((float) height - gapSize * columnSize - gapSize) / columnSize;
|
||||||
|
|
||||||
for (int y = 0; y < rowSize; y++) {
|
for (int y = 0; y < columnSize; y++) {
|
||||||
final float startY = y * cellHeight + y * gapSize + gapSize;
|
final float startY = y * cellHeight + y * gapSize + gapSize;
|
||||||
|
|
||||||
for (int x = 0; x < columnSize; x++) {
|
for (int x = 0; x < rowSize; x++) {
|
||||||
final float startX = x * cellWidth + x * gapSize + gapSize;
|
final float startX = x * cellWidth + x * gapSize + gapSize;
|
||||||
cells[x + y * columnSize] = new Cell(startX, startY, cellWidth, cellHeight);
|
cells[x + y * rowSize] = new Cell(startX, startY, cellWidth, cellHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.setOnMouseClicked(event -> {
|
canvas.setOnMouseClicked(event -> {
|
||||||
if (event.getButton() != MouseButton.PRIMARY) {
|
if (event.getButton() != MouseButton.PRIMARY) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int column = (int)((event.getX() / this.width) * columnSize);
|
final int column = (int) ((event.getX() / this.width) * rowSize);
|
||||||
final int row = (int)((event.getY() / this.height) * rowSize);
|
final int row = (int) ((event.getY() / this.height) * columnSize);
|
||||||
|
|
||||||
final Cell cell = cells[column + row * columnSize];
|
final Cell cell = cells[column + row * rowSize];
|
||||||
|
|
||||||
if (cell.isInside(event.getX(), event.getY())) {
|
if (cell.isInside(event.getX(), event.getY())) {
|
||||||
event.consume();
|
event.consume();
|
||||||
onCellClicked.accept(column + row * columnSize);
|
onCellClicked.accept(column + row * rowSize);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
private void render() {
|
||||||
graphics.clearRect(0, 0, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearCell(int cellIndex) {
|
|
||||||
Cell cell = cells[cellIndex];
|
|
||||||
graphics.clearRect(cell.x, cell.y, cell.width, cell.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void render() {
|
|
||||||
graphics.setFill(color);
|
graphics.setFill(color);
|
||||||
|
|
||||||
for (int x = 0; x < columnSize - 1; x++) {
|
for (int x = 0; x < rowSize - 1; x++) {
|
||||||
final float start = cells[x].x + cells[x].width;
|
final float start = cells[x].x + cells[x].width;
|
||||||
graphics.fillRect(start, gapSize, gapSize, height - gapSize * 2);
|
graphics.fillRect(start, gapSize, gapSize, height - gapSize * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < rowSize; y++) {
|
for (int y = 0; y < columnSize - 1; y++) {
|
||||||
final float start = cells[y * rowSize].y + cells[y * rowSize].height;
|
final float start = cells[y * rowSize].y + cells[y * rowSize].height;
|
||||||
graphics.fillRect(gapSize, start, width - gapSize * 2, gapSize);
|
graphics.fillRect(gapSize, start, width - gapSize * 2, gapSize);
|
||||||
}
|
}
|
||||||
@@ -121,32 +115,72 @@ public abstract class GameCanvas {
|
|||||||
graphics.fillRect(x, y, width, height);
|
graphics.fillRect(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawX(Color color, int cell) {
|
public void clear(int cell) {
|
||||||
graphics.setStroke(color);
|
final float x = cells[cell].x();
|
||||||
graphics.setLineWidth(gapSize);
|
final float y = cells[cell].y();
|
||||||
|
|
||||||
final float x = cells[cell].x() + gapSize;
|
final float width = cells[cell].width();
|
||||||
final float y = cells[cell].y() + gapSize;
|
final float height = cells[cell].height();
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
graphics.clearRect(x, y, width, height);
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
}
|
||||||
|
|
||||||
graphics.strokeLine(x, y, x + width, y + height);
|
public void clearAll() {
|
||||||
graphics.strokeLine(x + width, y, x, y + height);
|
for (int i = 0; i < cells.length; i++) {
|
||||||
}
|
clear(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void drawO(Color color, int cell) {
|
public void drawDot(Color color, int cell) {
|
||||||
graphics.setStroke(color);
|
final float x = cells[cell].x() + gapSize;
|
||||||
graphics.setLineWidth(gapSize);
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
final float x = cells[cell].x() + gapSize;
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
final float y = cells[cell].y() + gapSize;
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
graphics.setFill(color);
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
graphics.fillOval(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
graphics.strokeOval(x, y, 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;
|
||||||
|
|
||||||
|
final float fullWidth = cells[cell].width() - gapSize * 2;
|
||||||
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
|
final float scaledWidth = (float)(fullWidth * scale);
|
||||||
|
final float offsetX = (fullWidth - scaledWidth) / 2;
|
||||||
|
|
||||||
|
graphics.setFill(color);
|
||||||
|
graphics.fillOval(cx + offsetX, cy, scaledWidth, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeline flipDot(Color fromColor, Color toColor, int cell) {
|
||||||
|
final int steps = 60;
|
||||||
|
final long duration = 250;
|
||||||
|
final double interval = duration / (double) steps;
|
||||||
|
|
||||||
|
final Timeline timeline = new Timeline();
|
||||||
|
|
||||||
|
for (int i = 0; i <= steps; i++) {
|
||||||
|
final double t = i / (double) steps;
|
||||||
|
final KeyFrame keyFrame = new KeyFrame(Duration.millis(i * interval),
|
||||||
|
_ -> {
|
||||||
|
clear(cell);
|
||||||
|
|
||||||
|
final double scale = t <= 0.5 ? 1 - 2 * t : 2 * t - 1;
|
||||||
|
final Color currentColor = t < 0.5 ? fromColor : toColor;
|
||||||
|
|
||||||
|
drawDotScaled(currentColor, cell, scale);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
timeline.getKeyFrames().add(keyFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
public Canvas getCanvas() {
|
public Canvas getCanvas() {
|
||||||
return canvas;
|
return canvas;
|
||||||
|
|||||||
@@ -10,17 +10,6 @@ public final class ReversiCanvas extends GameCanvas {
|
|||||||
drawStartingDots();
|
drawStartingDots();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawDot(Color color, int cell) {
|
|
||||||
final float x = cells[cell].x() + gapSize;
|
|
||||||
final float y = cells[cell].y() + gapSize;
|
|
||||||
|
|
||||||
final float width = cells[cell].width() - gapSize * 2;
|
|
||||||
final float height = cells[cell].height() - gapSize * 2;
|
|
||||||
|
|
||||||
graphics.setFill(color);
|
|
||||||
graphics.fillOval(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawStartingDots() {
|
public void drawStartingDots() {
|
||||||
drawDot(Color.BLACK, 28);
|
drawDot(Color.BLACK, 28);
|
||||||
drawDot(Color.WHITE, 36);
|
drawDot(Color.WHITE, 36);
|
||||||
|
|||||||
@@ -9,5 +9,30 @@ public final class TicTacToeCanvas extends GameCanvas {
|
|||||||
super(color, width, height, 3, 3, 30, false, onCellClicked);
|
super(color, width, height, 3, 3, 30, false, onCellClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void drawX(Color color, int cell) {
|
||||||
|
graphics.setStroke(color);
|
||||||
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
|
final float x = cells[cell].x() + gapSize;
|
||||||
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
|
graphics.strokeLine(x, y, x + width, y + height);
|
||||||
|
graphics.strokeLine(x + width, y, x, y + height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawO(Color color, int cell) {
|
||||||
|
graphics.setStroke(color);
|
||||||
|
graphics.setLineWidth(gapSize);
|
||||||
|
|
||||||
|
final float x = cells[cell].x() + gapSize;
|
||||||
|
final float y = cells[cell].y() + gapSize;
|
||||||
|
|
||||||
|
final float width = cells[cell].width() - gapSize * 2;
|
||||||
|
final float height = cells[cell].height() - gapSize * 2;
|
||||||
|
|
||||||
|
graphics.strokeOval(x, y, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ public class Connect4Game {
|
|||||||
private final GameView view;
|
private final GameView view;
|
||||||
private final Connect4Canvas canvas;
|
private final Connect4Canvas canvas;
|
||||||
|
|
||||||
private AtomicBoolean isRunning;
|
private final AtomicBoolean isRunning;
|
||||||
|
|
||||||
public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
||||||
this.information = information;
|
this.information = information;
|
||||||
@@ -101,14 +101,8 @@ public class Connect4Game {
|
|||||||
}
|
}
|
||||||
private void localGameThread() {
|
private void localGameThread() {
|
||||||
while (isRunning.get()) {
|
while (isRunning.get()) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
final int currentTurn = game.getCurrentTurn();
|
||||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
setGameLabels(information.players[currentTurn].isHuman);
|
||||||
final int nextTurn = (currentTurn + 1) % GameInformation.Type.playerCount(information.type);
|
|
||||||
|
|
||||||
view.nextPlayer(information.players[currentTurn].isHuman,
|
|
||||||
information.players[currentTurn].name,
|
|
||||||
String.valueOf(currentValue),
|
|
||||||
information.players[nextTurn].name);
|
|
||||||
|
|
||||||
Game.Move move = null;
|
Game.Move move = null;
|
||||||
|
|
||||||
@@ -194,9 +188,9 @@ public class Connect4Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (move.value() == 'X') {
|
if (move.value() == 'X') {
|
||||||
canvas.drawX(Color.INDIANRED, move.position());
|
canvas.drawDot(Color.INDIANRED, move.position());
|
||||||
} else if (move.value() == 'O') {
|
} else if (move.value() == 'O') {
|
||||||
canvas.drawO(Color.ROYALBLUE, move.position());
|
canvas.drawDot(Color.ROYALBLUE, move.position());
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCanvas();
|
updateCanvas();
|
||||||
@@ -239,25 +233,24 @@ public class Connect4Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCanvas() {
|
private void updateCanvas() {
|
||||||
canvas.clear();
|
canvas.clearAll();
|
||||||
canvas.render();
|
|
||||||
|
|
||||||
for (int i = 0; i < game.board.length; i++) {
|
for (int i = 0; i < game.board.length; i++) {
|
||||||
if (game.board[i] == 'X') {
|
if (game.board[i] == 'X') {
|
||||||
canvas.drawX(Color.RED, i);
|
canvas.drawDot(Color.RED, i);
|
||||||
} else if (game.board[i] == 'O') {
|
} else if (game.board[i] == 'O') {
|
||||||
canvas.drawO(Color.BLUE, i);
|
canvas.drawDot(Color.BLUE, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setGameLabels(boolean isMe) {
|
private void setGameLabels(boolean isMe) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
final int currentTurn = game.getCurrentTurn();
|
||||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
final String currentValue = currentTurn == 0? "RED" : "BLUE";
|
||||||
|
|
||||||
view.nextPlayer(isMe,
|
view.nextPlayer(isMe,
|
||||||
information.players[isMe? 0 : 1].name,
|
information.players[isMe? 0 : 1].name,
|
||||||
String.valueOf(currentValue),
|
currentValue,
|
||||||
information.players[isMe? 1 : 0].name);
|
information.players[isMe? 1 : 0].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.toop.app.game;
|
package org.toop.app.game;
|
||||||
|
|
||||||
|
import javafx.animation.SequentialTransition;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.util.Duration;
|
||||||
import org.toop.app.App;
|
import org.toop.app.App;
|
||||||
import org.toop.app.GameInformation;
|
import org.toop.app.GameInformation;
|
||||||
import org.toop.app.canvas.ReversiCanvas;
|
import org.toop.app.canvas.ReversiCanvas;
|
||||||
@@ -33,6 +36,7 @@ public final class ReversiGame {
|
|||||||
private final ReversiCanvas canvas;
|
private final ReversiCanvas canvas;
|
||||||
|
|
||||||
private final AtomicBoolean isRunning;
|
private final AtomicBoolean isRunning;
|
||||||
|
private final AtomicBoolean isPaused;
|
||||||
|
|
||||||
public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
||||||
this.information = information;
|
this.information = information;
|
||||||
@@ -44,6 +48,7 @@ public final class ReversiGame {
|
|||||||
ai = new ReversiAI();
|
ai = new ReversiAI();
|
||||||
|
|
||||||
isRunning = new AtomicBoolean(true);
|
isRunning = new AtomicBoolean(true);
|
||||||
|
isPaused = new AtomicBoolean(false);
|
||||||
|
|
||||||
if (onForfeit == null || onExit == null) {
|
if (onForfeit == null || onExit == null) {
|
||||||
view = new GameView(null, () -> {
|
view = new GameView(null, () -> {
|
||||||
@@ -93,7 +98,7 @@ public final class ReversiGame {
|
|||||||
setGameLabels(myTurn == 0);
|
setGameLabels(myTurn == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCanvas();
|
updateCanvas(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReversiGame(GameInformation information) {
|
public ReversiGame(GameInformation information) {
|
||||||
@@ -102,14 +107,16 @@ public final class ReversiGame {
|
|||||||
|
|
||||||
private void localGameThread() {
|
private void localGameThread() {
|
||||||
while (isRunning.get()) {
|
while (isRunning.get()) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
if (isPaused.get()) {
|
||||||
final char currentValue = currentTurn == 0? 'B' : 'W';
|
try {
|
||||||
final int nextTurn = (currentTurn + 1) % GameInformation.Type.playerCount(information.type);
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException _) {}
|
||||||
|
|
||||||
view.nextPlayer(information.players[currentTurn].isHuman,
|
continue;
|
||||||
information.players[currentTurn].name,
|
}
|
||||||
String.valueOf(currentValue),
|
|
||||||
information.players[nextTurn].name);
|
final int currentTurn = game.getCurrentTurn();
|
||||||
|
setGameLabels(information.players[currentTurn].isHuman);
|
||||||
|
|
||||||
Game.Move move = null;
|
Game.Move move = null;
|
||||||
|
|
||||||
@@ -146,7 +153,7 @@ public final class ReversiGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Game.State state = game.play(move);
|
final Game.State state = game.play(move);
|
||||||
updateCanvas();
|
updateCanvas(true);
|
||||||
|
|
||||||
if (state != Game.State.NORMAL) {
|
if (state != Game.State.NORMAL) {
|
||||||
if (state == Game.State.WIN) {
|
if (state == Game.State.WIN) {
|
||||||
@@ -188,7 +195,7 @@ public final class ReversiGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCanvas();
|
updateCanvas(false);
|
||||||
setGameLabels(game.getCurrentTurn() == myTurn);
|
setGameLabels(game.getCurrentTurn() == myTurn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,11 +231,9 @@ public final class ReversiGame {
|
|||||||
view.updateChat(msg.message());
|
view.updateChat(msg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCanvas() {
|
private void updateCanvas(boolean animate) {
|
||||||
// Todo: this is very inefficient. still very fast but if the grid is bigger it might cause issues. improve.
|
// Todo: this is very inefficient. still very fast but if the grid is bigger it might cause issues. improve.
|
||||||
|
canvas.clearAll();
|
||||||
canvas.clear();
|
|
||||||
canvas.render();
|
|
||||||
|
|
||||||
for (int i = 0; i < game.board.length; i++) {
|
for (int i = 0; i < game.board.length; i++) {
|
||||||
if (game.board[i] == 'B') {
|
if (game.board[i] == 'B') {
|
||||||
@@ -238,20 +243,44 @@ public final class ReversiGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final Game.Move[] legalMoves = game.getLegalMoves();
|
final Game.Move[] flipped = game.getMostRecentlyFlippedPieces();
|
||||||
|
|
||||||
for (final Game.Move legalMove : legalMoves) {
|
final SequentialTransition animation = new SequentialTransition();
|
||||||
canvas.drawLegalPosition(legalMove.position());
|
isPaused.set(true);
|
||||||
|
|
||||||
|
if (animate && flipped != null) {
|
||||||
|
for (final Game.Move flip : flipped) {
|
||||||
|
canvas.clear(flip.position());
|
||||||
|
|
||||||
|
final Color from = flip.value() == 'W' ? Color.BLACK : Color.WHITE;
|
||||||
|
final Color to = flip.value() == 'W' ? Color.WHITE : Color.BLACK;
|
||||||
|
|
||||||
|
canvas.drawDot(from, flip.position());
|
||||||
|
|
||||||
|
animation.getChildren().addFirst(canvas.flipDot(from, to, flip.position()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animation.setOnFinished(_ -> {
|
||||||
|
isPaused.set(false);
|
||||||
|
|
||||||
|
final Game.Move[] legalMoves = game.getLegalMoves();
|
||||||
|
|
||||||
|
for (final Game.Move legalMove : legalMoves) {
|
||||||
|
canvas.drawLegalPosition(legalMove.position());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
animation.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setGameLabels(boolean isMe) {
|
private void setGameLabels(boolean isMe) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
final int currentTurn = game.getCurrentTurn();
|
||||||
final char currentValue = currentTurn == 0? 'B' : 'W';
|
final String currentValue = currentTurn == 0? "BLACK" : "WHITE";
|
||||||
|
|
||||||
view.nextPlayer(isMe,
|
view.nextPlayer(isMe,
|
||||||
information.players[isMe? 0 : 1].name,
|
information.players[isMe? 0 : 1].name,
|
||||||
String.valueOf(currentValue),
|
currentValue,
|
||||||
information.players[isMe? 1 : 0].name);
|
information.players[isMe? 1 : 0].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ public final class TicTacToeGame {
|
|||||||
private final GameView view;
|
private final GameView view;
|
||||||
private final TicTacToeCanvas canvas;
|
private final TicTacToeCanvas canvas;
|
||||||
|
|
||||||
private AtomicBoolean isRunning;
|
private final AtomicBoolean isRunning;
|
||||||
|
|
||||||
public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) {
|
||||||
this.information = information;
|
this.information = information;
|
||||||
@@ -101,13 +101,7 @@ public final class TicTacToeGame {
|
|||||||
private void localGameThread() {
|
private void localGameThread() {
|
||||||
while (isRunning.get()) {
|
while (isRunning.get()) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
final int currentTurn = game.getCurrentTurn();
|
||||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
setGameLabels(information.players[currentTurn].isHuman);
|
||||||
final int nextTurn = (currentTurn + 1) % GameInformation.Type.playerCount(information.type);
|
|
||||||
|
|
||||||
view.nextPlayer(information.players[currentTurn].isHuman,
|
|
||||||
information.players[currentTurn].name,
|
|
||||||
String.valueOf(currentValue),
|
|
||||||
information.players[nextTurn].name);
|
|
||||||
|
|
||||||
Game.Move move = null;
|
Game.Move move = null;
|
||||||
|
|
||||||
@@ -234,11 +228,11 @@ public final class TicTacToeGame {
|
|||||||
|
|
||||||
private void setGameLabels(boolean isMe) {
|
private void setGameLabels(boolean isMe) {
|
||||||
final int currentTurn = game.getCurrentTurn();
|
final int currentTurn = game.getCurrentTurn();
|
||||||
final char currentValue = currentTurn == 0? 'X' : 'O';
|
final String currentValue = currentTurn == 0? "X" : "O";
|
||||||
|
|
||||||
view.nextPlayer(isMe,
|
view.nextPlayer(isMe,
|
||||||
information.players[isMe? 0 : 1].name,
|
information.players[isMe? 0 : 1].name,
|
||||||
String.valueOf(currentValue),
|
currentValue,
|
||||||
information.players[isMe? 1 : 0].name);
|
information.players[isMe? 1 : 0].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user