(RANDOM COMMIT) Hope it works

This commit is contained in:
2025-11-28 15:04:12 +01:00
parent 3b6017b369
commit f69df69a36
21 changed files with 524 additions and 88 deletions

View File

@@ -4,35 +4,48 @@ import javafx.scene.paint.Color;
import java.util.function.Consumer;
public final class TicTacToeCanvas extends GameCanvas {
public final class TicTacToeCanvas extends GameCanvas implements Drawable {
public TicTacToeCanvas(Color color, int width, int height, Consumer<Integer> onCellClicked) {
super(color, Color.TRANSPARENT, width, height, 3, 3, 30, false, onCellClicked,null);
}
public void drawX(Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
public void drawPlayer(char a, Color color, int cell) {
graphics.setStroke(color);
graphics.setLineWidth(gapSize);
final float x = cells[cell].x() + gapSize;
final float y = cells[cell].y() + gapSize;
drawChar(a, color, cell);
draw();
}
final float width = cells[cell].width() - gapSize * 2;
final float height = cells[cell].height() - gapSize * 2;
// 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);
// }
graphics.strokeLine(x, y, x + width, y + height);
graphics.strokeLine(x + width, y, x, y + height);
}
@Override
public void draw() {
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);
}
}
//
// 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);
// }
}

View File

@@ -1,8 +0,0 @@
package org.toop.app.game;
import org.toop.game.records.Move;
public interface MoveBehaviour
{
int getMove();
}

View File

@@ -1,6 +0,0 @@
package org.toop.app.game;
import org.toop.game.records.Move;
public abstract class Player implements MoveBehaviour{
}

View File

@@ -0,0 +1,45 @@
package org.toop.app.game.Players;
import org.toop.game.AIR;
import org.toop.game.GameR;
/**
* Represents a player controlled by an AI in a game.
* <p>
* This player uses an {@link AIR} instance to determine its moves. The generic
* parameter {@code T} specifies the type of {@link GameR} the AI can handle.
* </p>
*
* @param <T> the specific type of game this AI player can play
*/
public class ArtificialPlayer<T extends GameR> extends Player {
/** The AI instance used to calculate moves. */
private final AIR<T> ai;
/**
* Constructs a new ArtificialPlayer using the specified AI.
*
* @param ai the AI instance that determines moves for this player
*/
public ArtificialPlayer(AIR<T> ai) {
this.ai = ai;
}
/**
* Determines the next move for this player using its AI.
* <p>
* This method overrides {@link Player#getMove(GameR)}. Because the AI is
* typed to {@code T}, a runtime cast is required. It is the caller's
* responsibility to ensure that {@code gameCopy} is of type {@code T}.
* </p>
*
* @param gameCopy a copy of the current game state
* @return the integer representing the chosen move
* @throws ClassCastException if {@code gameCopy} is not of type {@code T}
*/
@Override
public int getMove(GameR gameCopy) {
return ai.findBestMove((T) gameCopy, 9); // TODO: Make depth configurable
}
}

View File

@@ -1,15 +1,17 @@
package org.toop.app.game;
package org.toop.app.game.Players;
import org.toop.game.GameR;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class LocalPlayer extends Player{
private BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
public class LocalPlayer extends Player {
private final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
public LocalPlayer() {}
@Override
public int getMove() {
public int getMove(GameR gameCopy) {
try {
return queue.take();
}catch (InterruptedException e){

View File

@@ -0,0 +1,23 @@
package org.toop.app.game.Players;
import org.toop.game.GameR;
/**
* Interface representing an entity capable of making a move in a game.
* <p>
* Any class implementing this interface should provide logic to determine
* the next move given a snapshot of the current game state.
* </p>
*/
public interface MakesMove {
/**
* Determines the next move based on the provided game state.
*
* @param gameCopy a copy or snapshot of the current game state
* (never null)
* @return an integer representing the chosen move.
* The interpretation of this value depends on the specific game.
*/
int getMove(GameR gameCopy);
}

View File

@@ -0,0 +1,24 @@
package org.toop.app.game.Players;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Represents a player controlled remotely or over a network.
* <p>
* This class extends {@link Player} and can be used to implement game logic
* where moves are provided by an external source (e.g., another user or a server).
* Currently, this class is a placeholder and does not implement move logic.
* </p>
*/
public class OnlinePlayer extends Player {
/**
* Constructs a new OnlinePlayer.
* <p>
* Currently, no additional initialization is performed. Subclasses or
* future implementations should provide mechanisms to receive moves from
* an external source.
*/
public OnlinePlayer() {}
}

View File

@@ -0,0 +1,35 @@
package org.toop.app.game.Players;
import org.toop.game.GameR;
/**
* Abstract class representing a player in a game.
* <p>
* Players are entities that can make moves based on the current state of a game.
* This class implements {@link MakesMove} and serves as a base for concrete
* player types, such as human players or AI players.
* </p>
* <p>
* Subclasses should override the {@link #getMove(GameR)} method to provide
* specific move logic.
* </p>
*/
public abstract class Player implements MakesMove {
/**
* Determines the next move based on the provided game state.
* <p>
* The default implementation throws an {@link UnsupportedOperationException},
* indicating that concrete subclasses must override this method to provide
* actual move logic.
* </p>
*
* @param gameCopy a snapshot of the current game state
* @return an integer representing the chosen move
* @throws UnsupportedOperationException if the method is not overridden
*/
@Override
public int getMove(GameR gameCopy) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@@ -1,92 +0,0 @@
package org.toop.app.game;
import javafx.geometry.Pos;
import javafx.scene.paint.Color;
import org.toop.app.App;
import org.toop.app.canvas.GameCanvas;
import org.toop.app.canvas.TicTacToeCanvas;
import org.toop.app.widget.WidgetContainer;
import org.toop.app.widget.view.GameView;
import org.toop.game.TurnBasedGameR;
import org.toop.game.enumerators.GameState;
import org.toop.game.records.Move;
import org.toop.game.tictactoe.TicTacToe;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
public class TurnBasedGameThread implements Runnable {
private final Player[] players; // List of players, can't be changed.
private final TurnBasedGameR game; // Reference to game instance
private final AtomicBoolean isRunning = new AtomicBoolean();
// TODO: Seperate this from game Thread
private final GameView primary = new GameView(null, null, null);
private final TicTacToeCanvas canvas;
public TurnBasedGameThread(Player[] players, TurnBasedGameR game) {
// Make sure player list matches expected size
if (players.length != game.getPlayerCount()){
throw new IllegalArgumentException("players and game's players must have same length");
}
this.players = players;
this.game = game;
Thread thread = new Thread(this::run);
thread.start();
// UI SHIZ TO MOVE
canvas = new TicTacToeCanvas(Color.GRAY,
(App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,(c) -> {if (players[game.getCurrentTurn()] instanceof LocalPlayer lp) {lp.enqueueMove(c);}});
primary.add(Pos.CENTER, canvas.getCanvas());
WidgetContainer.getCurrentView().transitionNext(primary);
}
// Move to UI shiz
private void drawMove(int move) {
if (game.getCurrentTurn() == 1) canvas.drawX(Color.RED, move);
else canvas.drawO(Color.BLUE, move);
}
public void run() {
isRunning.set(true);
// Game logic loop
while(isRunning.get()) {
// Get current player
Player currentPlayer = players[game.getCurrentTurn()];
// Get this player's valid moves
Integer[] validMoves = game.getLegalMoves();
// Get player's move, reask if Move is invalid
// TODO: Limit amount of retries?
int move = currentPlayer.getMove();
while (!Arrays.asList(validMoves).contains(move)) {
System.out.println("Invalid move");;
move = currentPlayer.getMove();
}
// Make move
GameState state = game.play(move);
drawMove(move);
if (state != GameState.NORMAL) {
if (state == GameState.WIN) {
// Someone won
} else if (state == GameState.DRAW) {
// THere was a draw
}
isRunning.set(false);
}
}
}
private void updateUI(){
}
}

View File

@@ -2,12 +2,15 @@ package org.toop.app.widget.view;
import org.toop.app.GameInformation;
import org.toop.app.game.*;
import org.toop.app.game.Players.ArtificialPlayer;
import org.toop.app.game.Players.LocalPlayer;
import org.toop.app.game.Players.Player;
import org.toop.app.widget.Primitive;
import org.toop.app.widget.complex.PlayerInfoWidget;
import org.toop.app.widget.complex.ViewWidget;
import org.toop.app.widget.popup.ErrorPopup;
import org.toop.game.tictactoe.TicTacToe;
import org.toop.game.tictactoe.TicTacToeAI;
import org.toop.game.TurnBasedGameThread;
import org.toop.game.tictactoe.TicTacToeAIR;
import org.toop.game.tictactoe.TicTacToeR;
import org.toop.local.AppContext;
@@ -33,7 +36,7 @@ public class LocalMultiplayerView extends ViewWidget {
}
switch (information.type) {
case TICTACTOE -> new TurnBasedGameThread(new Player[]{new LocalPlayer(), new LocalPlayer()}, new TicTacToeR());
case TICTACTOE -> new TurnBasedGameThread(new Player[]{new LocalPlayer(), new ArtificialPlayer<>(new TicTacToeAIR())}, new TicTacToeR());
case REVERSI -> new ReversiGame(information);
case CONNECT4 -> new Connect4Game(information);
// case BATTLESHIP -> new BattleshipGame(information);