Merge branch 'Development' into ReversiML

# Conflicts:
#	game/src/main/java/org/toop/game/games/reversi/ReversiR.java
This commit is contained in:
Ticho Hidding
2025-12-13 15:04:40 +01:00
71 changed files with 1608 additions and 1823 deletions

View File

@@ -14,7 +14,8 @@ import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.events.ResponseToUniqueEvent;
import org.toop.framework.eventbus.events.UniqueEvent;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.eventbus.subscriber.DefaultSubscriber;
import org.toop.framework.eventbus.subscriber.DefaultNamedSubscriber;
import org.toop.framework.eventbus.subscriber.NamedSubscriber;
import org.toop.framework.eventbus.subscriber.Subscriber;
/**
@@ -43,7 +44,7 @@ public class EventFlow {
private EventType event = null;
/** The listener returned by GlobalEventBus subscription. Used for unsubscription. */
private final List<Subscriber<?, ?>> listeners = new ArrayList<>();
private final List<NamedSubscriber<?>> listeners = new ArrayList<>();
/** Holds the results returned from the subscribed event, if any. */
private Map<String, ?> result = null;
@@ -161,7 +162,7 @@ public class EventFlow {
this.result = eventClass.result();
};
var subscriber = new DefaultSubscriber<>(
var subscriber = new DefaultNamedSubscriber<>(
name,
event,
newAction
@@ -248,7 +249,7 @@ public class EventFlow {
}
};
var listener = new DefaultSubscriber<>(
var listener = new DefaultNamedSubscriber<>(
name,
(Class<TT>) action.getClass().getDeclaredMethods()[0].getParameterTypes()[0],
newAction
@@ -295,7 +296,7 @@ public class EventFlow {
if (unsubscribeAfterSuccess) unsubscribe(String.valueOf(id));
};
var listener = new DefaultSubscriber<>(
var listener = new DefaultNamedSubscriber<>(
name,
event,
newAction
@@ -378,7 +379,7 @@ public class EventFlow {
}
};
var listener = new DefaultSubscriber<>(
var listener = new DefaultNamedSubscriber<>(
name,
eventClass,
newAction
@@ -496,7 +497,7 @@ public class EventFlow {
*
* @return Copy of the list of listeners.
*/
public Subscriber<?, ?>[] getListeners() {
public Subscriber<?>[] getListeners() {
return listeners.toArray(new Subscriber[0]);
}

View File

@@ -3,6 +3,7 @@ package org.toop.framework.eventbus;
import org.apache.logging.log4j.LogManager;
import org.toop.framework.eventbus.bus.DisruptorEventBus;
import org.toop.framework.eventbus.bus.EventBus;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.store.DefaultSubscriberStore;
import org.toop.framework.eventbus.subscriber.Subscriber;
@@ -19,17 +20,17 @@ public class GlobalEventBus implements EventBus {
}
@Override
public void subscribe(Subscriber<?, ?> listener) {
public void subscribe(Subscriber<? extends EventType> listener) {
INSTANCE.subscribe(listener);
}
@Override
public void unsubscribe(Subscriber<?, ?> listener) {
public void unsubscribe(Subscriber<? extends EventType> listener) {
INSTANCE.unsubscribe(listener);
}
@Override
public <T> void post(T event) {
public <T extends EventType> void post(T event) {
INSTANCE.post(event);
}

View File

@@ -17,22 +17,22 @@ public class DefaultEventBus implements EventBus {
}
@Override
public void subscribe(Subscriber<?, ?> subscriber) {
public void subscribe(Subscriber<? extends EventType> subscriber) {
eventsHolder.add(subscriber);
}
@Override
public void unsubscribe(Subscriber<?, ?> subscriber) {
public void unsubscribe(Subscriber<? extends EventType> subscriber) {
eventsHolder.remove(subscriber);
}
@Override
@SuppressWarnings("unchecked")
public <T> void post(T event) {
public <T extends EventType> void post(T event) {
Class<T> eventType = (Class<T>) event.getClass();
var subs = eventsHolder.get(eventType);
if (subs != null) {
for (Subscriber<?, ?> subscriber : subs) {
for (Subscriber<?> subscriber : subs) {
Class<T> eventClass = (Class<T>) subscriber.event();
Consumer<EventType> action = (Consumer<EventType>) subscriber.handler();

View File

@@ -21,8 +21,8 @@ public class DisruptorEventBus implements EventBus {
private final Logger logger;
private final SubscriberStore eventsHolder;
private final Disruptor<EventHolder<?>> disruptor;
private final RingBuffer<EventHolder<?>> ringBuffer;
private final Disruptor<EventHolder<? extends EventType>> disruptor;
private final RingBuffer<EventHolder<? extends EventType>> ringBuffer;
public DisruptorEventBus(Logger logger, SubscriberStore eventsHolder) {
this.logger = logger;
@@ -41,9 +41,9 @@ public class DisruptorEventBus implements EventBus {
this.ringBuffer = disruptor.getRingBuffer();
}
private Disruptor<EventHolder<?>> getEventHolderDisruptor(ThreadFactory threadFactory) {
private Disruptor<EventHolder<? extends EventType>> getEventHolderDisruptor(ThreadFactory threadFactory) {
int RING_BUFFER_SIZE = 1024 * 64;
Disruptor<EventHolder<?>> disruptor = new Disruptor<>(
Disruptor<EventHolder<? extends EventType>> disruptor = new Disruptor<>(
EventHolder::new,
RING_BUFFER_SIZE,
threadFactory,
@@ -61,17 +61,17 @@ public class DisruptorEventBus implements EventBus {
}
@Override
public void subscribe(Subscriber<?, ?> listener) {
public void subscribe(Subscriber<? extends EventType> listener) {
eventsHolder.add(listener);
}
@Override
public void unsubscribe(Subscriber<?, ?> listener) {
public void unsubscribe(Subscriber<? extends EventType> listener) {
eventsHolder.remove(listener);
}
@Override
public <T> void post(T event) {
public <T extends EventType> void post(T event) {
long seq = ringBuffer.next();
try {
@SuppressWarnings("unchecked")
@@ -93,10 +93,10 @@ public class DisruptorEventBus implements EventBus {
eventsHolder.reset();
}
private <T> void dispatchEvent(T event) {
private <T extends EventType> void dispatchEvent(T event) {
var classListeners = eventsHolder.get(event.getClass());
if (classListeners != null) {
for (Subscriber<?, ?> listener : classListeners) {
for (Subscriber<?> listener : classListeners) {
try {
callListener(listener, event);
} catch (Throwable e) {
@@ -108,7 +108,7 @@ public class DisruptorEventBus implements EventBus {
@SuppressWarnings("unchecked")
private <T> void callListener(Subscriber<?, ?> subscriber, T event) {
private <T> void callListener(Subscriber<?> subscriber, T event) {
Class<T> eventClass = (Class<T>) subscriber.event();
Consumer<EventType> action = (Consumer<EventType>) subscriber.handler();

View File

@@ -1,11 +1,12 @@
package org.toop.framework.eventbus.bus;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.subscriber.Subscriber;
public interface EventBus {
void subscribe(Subscriber<?, ?> subscriber);
void unsubscribe(Subscriber<?, ?> subscriber);
<T> void post(T event);
void subscribe(Subscriber<? extends EventType> subscriber);
void unsubscribe(Subscriber<? extends EventType> subscriber);
<T extends EventType> void post(T event);
void shutdown();
void reset();
}

View File

@@ -1,23 +1,24 @@
package org.toop.framework.eventbus.store;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.subscriber.Subscriber;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class AsyncSubscriberStore implements SubscriberStore {
private final ConcurrentHashMap<Class<?>, ConcurrentLinkedQueue<Subscriber<?, ?>>> queues = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<?>, Subscriber<?, ?>[]> snapshots = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<? extends EventType>, ConcurrentLinkedQueue<Subscriber<? extends EventType>>> queues = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<? extends EventType>, Subscriber<? extends EventType>[]> snapshots = new ConcurrentHashMap<>();
@Override
public void add(Subscriber<?, ?> sub) {
public void add(Subscriber<? extends EventType> sub) {
queues.computeIfAbsent(sub.event(), _ -> new ConcurrentLinkedQueue<>()).add(sub);
rebuildSnapshot(sub.event());
}
@Override
public void remove(Subscriber<?, ?> sub) {
ConcurrentLinkedQueue<Subscriber<?, ?>> queue = queues.get(sub.event());
public void remove(Subscriber<? extends EventType> sub) {
ConcurrentLinkedQueue<Subscriber<?>> queue = queues.get(sub.event());
if (queue != null) {
queue.remove(sub);
rebuildSnapshot(sub.event());
@@ -25,8 +26,8 @@ public class AsyncSubscriberStore implements SubscriberStore {
}
@Override
public Subscriber<?, ?>[] get(Class<?> event) {
return snapshots.getOrDefault(event, new Subscriber[0]);
public Subscriber<? extends EventType>[] get(Class<? extends EventType> event) {
return snapshots.getOrDefault(event, new Subscriber<?>[0]);
}
@Override
@@ -35,12 +36,12 @@ public class AsyncSubscriberStore implements SubscriberStore {
snapshots.clear();
}
private void rebuildSnapshot(Class<?> event) {
ConcurrentLinkedQueue<Subscriber<?, ?>> queue = queues.get(event);
private void rebuildSnapshot(Class<? extends EventType> event) {
ConcurrentLinkedQueue<Subscriber<?>> queue = queues.get(event);
if (queue != null) {
snapshots.put(event, queue.toArray(new Subscriber[0]));
snapshots.put(event, queue.toArray(new Subscriber<?>[0]));
} else {
snapshots.put(event, new Subscriber[0]);
snapshots.put(event, new Subscriber<?>[0]);
}
}
}

View File

@@ -1,25 +1,27 @@
package org.toop.framework.eventbus.store;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.subscriber.NamedSubscriber;
import org.toop.framework.eventbus.subscriber.Subscriber;
import java.util.concurrent.ConcurrentHashMap;
public class DefaultSubscriberStore implements SubscriberStore {
private static final Subscriber<?, ?>[] EMPTY = new Subscriber[0];
private static final Subscriber<? extends EventType>[] EMPTY = new Subscriber<?>[0];
private final ConcurrentHashMap<Class<?>, Subscriber<?, ?>[]> listeners =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<Class<? extends EventType>, Subscriber<? extends EventType>[]>
listeners = new ConcurrentHashMap<>();
@Override
public void add(Subscriber<?, ?> sub) {
public void add(Subscriber<? extends EventType> sub) {
listeners.compute(sub.event(), (_, arr) -> {
if (arr == null || arr.length == 0) {
return new Subscriber<?, ?>[]{sub};
return new Subscriber<?>[]{sub};
}
int len = arr.length;
Subscriber<?, ?>[] newArr = new Subscriber[len + 1];
Subscriber<?>[] newArr = new Subscriber[len + 1];
System.arraycopy(arr, 0, newArr, 0, len);
newArr[len] = sub;
return newArr;
@@ -27,7 +29,7 @@ public class DefaultSubscriberStore implements SubscriberStore {
}
@Override
public void remove(Subscriber<?, ?> sub) {
public void remove(Subscriber<? extends EventType> sub) {
listeners.computeIfPresent(sub.event(), (_, arr) -> {
int len = arr.length;
@@ -36,7 +38,7 @@ public class DefaultSubscriberStore implements SubscriberStore {
}
int keep = 0;
for (Subscriber<?, ?> s : arr) {
for (Subscriber<?> s : arr) {
if (!s.equals(sub)) keep++;
}
@@ -47,9 +49,9 @@ public class DefaultSubscriberStore implements SubscriberStore {
return null;
}
Subscriber<?, ?>[] newArr = new Subscriber[keep];
Subscriber<?>[] newArr = new Subscriber[keep];
int i = 0;
for (Subscriber<?, ?> s : arr) {
for (Subscriber<?> s : arr) {
if (!s.equals(sub)) {
newArr[i++] = s;
}
@@ -60,7 +62,7 @@ public class DefaultSubscriberStore implements SubscriberStore {
}
@Override
public Subscriber<?, ?>[] get(Class<?> event) {
public Subscriber<? extends EventType>[] get(Class<? extends EventType> event) {
return listeners.getOrDefault(event, EMPTY);
}

View File

@@ -1,10 +1,11 @@
package org.toop.framework.eventbus.store;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.subscriber.Subscriber;
public interface SubscriberStore {
void add(Subscriber<?, ?> subscriber);
void remove(Subscriber<?, ?> subscriber);
Subscriber<?, ?>[] get(Class<?> event);
void add(Subscriber<? extends EventType> subscriber);
void remove(Subscriber<? extends EventType> subscriber);
Subscriber<? extends EventType>[] get(Class<? extends EventType> event);
void reset();
}

View File

@@ -1,5 +1,6 @@
package org.toop.framework.eventbus.store;
import org.toop.framework.eventbus.events.EventType;
import org.toop.framework.eventbus.subscriber.Subscriber;
import java.util.ArrayList;
@@ -8,23 +9,23 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SyncSubscriberStore implements SubscriberStore {
private final Map<Class<?>, List<Subscriber<?, ?>>> LISTENERS = new ConcurrentHashMap<>();
private static final Subscriber<?, ?>[] EMPTY = new Subscriber[0];
private final Map<Class<? extends EventType>, List<Subscriber<? extends EventType>>> LISTENERS = new ConcurrentHashMap<>();
private static final Subscriber<? extends EventType>[] EMPTY = new Subscriber<?>[0];
@Override
public void add(Subscriber<?, ?> sub) {
public void add(Subscriber<? extends EventType> sub) {
LISTENERS.computeIfAbsent(sub.event(), _ -> new ArrayList<>()).add(sub);
}
@Override
public void remove(Subscriber<?, ?> sub) {
public void remove(Subscriber<? extends EventType> sub) {
LISTENERS.getOrDefault(sub.event(), new ArrayList<>()).remove(sub);
LISTENERS.entrySet().removeIf(entry -> entry.getValue().isEmpty());
}
@Override
public Subscriber<?, ?>[] get(Class<?> event) {
List<Subscriber<?, ?>> list = LISTENERS.get(event);
public Subscriber<? extends EventType>[] get(Class<? extends EventType> event) {
List<Subscriber<? extends EventType>> list = LISTENERS.get(event);
if (list == null || list.isEmpty()) return EMPTY;
return list.toArray(EMPTY);
}

View File

@@ -0,0 +1,8 @@
package org.toop.framework.eventbus.subscriber;
import org.toop.framework.eventbus.events.EventType;
import java.util.function.Consumer;
public record DefaultNamedSubscriber<K extends EventType>(String id, Class<K> event, Consumer<K> handler)
implements NamedSubscriber<K> {}

View File

@@ -1,5 +1,8 @@
package org.toop.framework.eventbus.subscriber;
import org.toop.framework.eventbus.events.EventType;
import java.util.function.Consumer;
public record DefaultSubscriber<K>(String id, Class<K> event, Consumer<K> handler) implements NamedSubscriber<K> {}
public record DefaultSubscriber<K extends EventType>(Class<K> event, Consumer<K> handler) implements Subscriber<K> {}

View File

@@ -0,0 +1,5 @@
package org.toop.framework.eventbus.subscriber;
public interface HasId<ID> {
ID id();
}

View File

@@ -1,3 +1,5 @@
package org.toop.framework.eventbus.subscriber;
public interface IdSubscriber<T> extends Subscriber<Long, T> {}
import org.toop.framework.eventbus.events.EventType;
public interface IdSubscriber<K extends EventType> extends Subscriber<K>, HasId<Long> {}

View File

@@ -1,5 +1,8 @@
package org.toop.framework.eventbus.subscriber;
import org.toop.framework.eventbus.events.EventType;
import java.util.function.Consumer;
public record LongIdSubscriber<K>(Long id, Class<K> event, Consumer<K> handler) implements IdSubscriber<K> {}
public record LongIdSubscriber<K extends EventType>(Long id, Class<K> event, Consumer<K> handler)
implements IdSubscriber<K> {}

View File

@@ -1,3 +1,5 @@
package org.toop.framework.eventbus.subscriber;
public interface NamedSubscriber<T> extends Subscriber<String, T> {}
import org.toop.framework.eventbus.events.EventType;
public interface NamedSubscriber<K extends EventType> extends Subscriber<K>, HasId<String> {}

View File

@@ -1,9 +1,10 @@
package org.toop.framework.eventbus.subscriber;
import org.toop.framework.eventbus.events.EventType;
import java.util.function.Consumer;
public interface Subscriber<ID, K> {
ID id();
public interface Subscriber<K extends EventType> {
Class<K> event();
Consumer<K> handler();
}

View File

@@ -0,0 +1,6 @@
package org.toop.framework.gameFramework;
@FunctionalInterface
public interface LongPairConsumer {
void accept(long a, long b);
}

View File

@@ -0,0 +1,18 @@
package org.toop.framework.gameFramework.controller;
import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay;
import org.toop.framework.gameFramework.model.game.threadBehaviour.Controllable;
import org.toop.framework.networking.events.NetworkEvents;
public interface GameController extends Controllable, UpdatesGameUI {
/** Called when it is this player's turn to make a move. */
void onYourTurn(NetworkEvents.YourTurnResponse event);
/** Called when a move from another player is received. */
void onMoveReceived(NetworkEvents.GameMoveResponse event);
/** Called when the game has finished, with the final result. */
void gameFinished(NetworkEvents.GameResultResponse event);
void sendMove(long clientId, long move);
}

View File

@@ -1,108 +0,0 @@
package org.toop.framework.gameFramework.model.game;
import org.toop.framework.gameFramework.model.player.Player;
import java.util.Arrays;
public abstract class AbstractGame<T extends TurnBasedGame<T>> implements TurnBasedGame<T> {
private final int playerCount; // How many players are playing
private final Player<T>[] players;
private int turn = 0; // What turn it is in the game
/** Constant representing an empty position on the board. */
public static final int EMPTY = -1;
/** Number of rows in the game board. */
private final int rowSize;
/** Number of columns in the game board. */
private final int columnSize;
/** The game board stored as a one-dimensional array. */
private final int[] board;
protected AbstractGame(int rowSize, int columnSize, int playerCount, Player<T>[] players) {
assert rowSize > 0 && columnSize > 0;
this.rowSize = rowSize;
this.columnSize = columnSize;
this.players = players;
board = new int[rowSize * columnSize];
Arrays.fill(board, EMPTY);
this.playerCount = playerCount;
}
protected AbstractGame(AbstractGame<T> other){
this.rowSize = other.rowSize;
this.columnSize = other.columnSize;
this.board = other.board.clone();
this.playerCount = other.playerCount;
this.turn = other.turn;
// TODO: Make this a deep copy, add deep copy interface to Player
this.players = other.players;
}
public static boolean contains(int[] array, int value) {
// O(n)
for (int element : array){
if (element == value) return true;
}
return false;
}
public Player<T> getPlayer(int index) {
return players[index];
}
public int getPlayerCount(){return this.playerCount;}
protected void nextTurn() {
turn += 1;
}
public int getCurrentTurn() {
return turn % playerCount;
}
protected void setBoard(int position) {
setBoard(position, getCurrentTurn());
}
protected void setBoard(int position, int player) {
this.board[position] = player;
}
/**
* Returns the number of rows in the board.
*
* @return number of rows
*/
public int getRowSize() {
return this.rowSize;
}
/**
* Returns the number of columns in the board.
*
* @return number of columns
*/
public int getColumnSize() {
return this.columnSize;
}
/**
* Returns a copy of the current board state.
*
* @return a cloned array representing the board
*/
public int[] getBoard() {
return this.board.clone();
}
}

View File

@@ -0,0 +1,5 @@
package org.toop.framework.gameFramework.model.game;
public interface BoardProvider {
long[] getBoard();
}

View File

@@ -1,5 +1,5 @@
package org.toop.framework.gameFramework.model.game;
public interface DeepCopyable<T extends TurnBasedGame<T>> {
public interface DeepCopyable<T> {
T deepCopy();
}

View File

@@ -12,7 +12,7 @@ public interface Playable {
*
* @return an array of integers representing legal moves
*/
int[] getLegalMoves();
long getLegalMoves();
/**
* Plays the given move and returns the resulting game state.
@@ -20,5 +20,5 @@ public interface Playable {
* @param move the move to apply
* @return the {@link GameState} and additional info after the move
*/
PlayResult play(int move);
PlayResult play(long move);
}

View File

@@ -10,11 +10,11 @@ import org.toop.framework.networking.events.NetworkEvents;
public interface SupportsOnlinePlay {
/** Called when it is this player's turn to make a move. */
void onYourTurn(NetworkEvents.YourTurnResponse event);
void onYourTurn(long clientId);
/** Called when a move from another player is received. */
void onMoveReceived(NetworkEvents.GameMoveResponse event);
void onMoveReceived(long move);
/** Called when the game has finished, with the final result. */
void gameFinished(NetworkEvents.GameResultResponse event);
void gameFinished(String condition);
}

View File

@@ -1,5 +1,7 @@
package org.toop.framework.gameFramework.model.game;
public interface TurnBasedGame<T extends TurnBasedGame<T>> extends Playable, DeepCopyable<T>, PlayerProvider<T> {
public interface TurnBasedGame<T extends TurnBasedGame<T>> extends Playable, DeepCopyable<T>, PlayerProvider<T>, BoardProvider {
int getCurrentTurn();
int getPlayerCount();
int getWinner();
}

View File

@@ -2,9 +2,12 @@ package org.toop.framework.gameFramework.model.game.threadBehaviour;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.gameFramework.LongPairConsumer;
import org.toop.framework.gameFramework.controller.GameController;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
/**
* Base class for thread-based game behaviours.
@@ -13,8 +16,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
* a running flag, a game reference, and a logger.
* Subclasses implement the actual game-loop logic.
*/
public abstract class AbstractThreadBehaviour<T extends TurnBasedGame<T>> implements ThreadBehaviour<T> {
public abstract class AbstractThreadBehaviour<T extends TurnBasedGame<T>> implements ThreadBehaviour {
private LongPairConsumer onSendMove;
private Runnable onUpdateUI;
/** Indicates whether the game loop or event processing is active. */
protected final AtomicBoolean isRunning = new AtomicBoolean();
@@ -32,4 +36,26 @@ public abstract class AbstractThreadBehaviour<T extends TurnBasedGame<T>> implem
public AbstractThreadBehaviour(T game) {
this.game = game;
}
protected void updateUI(){
if (onUpdateUI != null) {
onUpdateUI.run();
}
}
protected void sendMove(long clientId, long move){
if (onSendMove != null) {
onSendMove.accept(clientId, move);
}
}
@Override
public void setOnUpdateUI(Runnable onUpdateUI) {
this.onUpdateUI = onUpdateUI;
}
@Override
public void setOnSendMove(LongPairConsumer onSendMove) {
this.onSendMove = onSendMove;
}
}

View File

@@ -1,11 +1,18 @@
package org.toop.framework.gameFramework.model.game.threadBehaviour;
import org.toop.framework.gameFramework.LongPairConsumer;
import org.toop.framework.gameFramework.controller.GameController;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
import java.util.function.Consumer;
/**
* Strategy interface for controlling game thread behavior.
* <p>
* Defines how a game's execution is started, stopped, and which player is active.
*/
public interface ThreadBehaviour<T extends TurnBasedGame<T>> extends Controllable {
public interface ThreadBehaviour extends Controllable {
void setOnUpdateUI(Runnable onUpdateUI);
void setOnSendMove(LongPairConsumer onSendMove);
}

View File

@@ -0,0 +1,7 @@
package org.toop.framework.gameFramework.model.player;
import org.toop.framework.gameFramework.model.game.DeepCopyable;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
public interface AI<T extends TurnBasedGame<T>> extends MoveProvider<T>, DeepCopyable<AI<T>> {
}

View File

@@ -12,6 +12,6 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame;
*
* @param <T> the specific type of game this AI can play, extending {@link GameR}
*/
public abstract class AbstractAI<T extends TurnBasedGame> implements MoveProvider<T> {
public abstract class AbstractAI<T extends TurnBasedGame<T>> implements AI<T> {
// Concrete AI implementations should override findBestMove(T game, int depth)
}

View File

@@ -5,44 +5,66 @@ import org.apache.logging.log4j.Logger;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
/**
* Abstract class representing a player in a game.
* <p>
* Players are entities that can make moves based on the current state of a game.
* 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>
* Base class for players in a turn-based game.
*
* @param <T> the game type
*/
public abstract class AbstractPlayer<T extends TurnBasedGame<T>> implements Player<T> {
private int playerIndex = -1;
private Logger logger = LogManager.getLogger(this.getClass());
private final Logger logger = LogManager.getLogger(this.getClass());
private final String name;
/**
* Creates a new player with the given name.
*
* @param name the player name
*/
protected AbstractPlayer(String name) {
this.name = name;
}
/**
* 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>
* Creates a copy of another player.
*
* @param gameCopy a snapshot of the current game state
* @return an integer representing the chosen move
* @throws UnsupportedOperationException if the method is not overridden
* @param other the player to copy
*/
public int getMove(T gameCopy) {
logger.error("Method getMove not implemented.");
throw new UnsupportedOperationException("Not supported yet.");
protected AbstractPlayer(AbstractPlayer<T> other) {
this.name = other.name;
}
public String getName(){
/**
* Gets the player's move for the given game state.
* A deep copy is provided so the player cannot modify the real state.
* <p>
* This method uses the Template Method Pattern: it defines the fixed
* algorithm and delegates the variable part to {@link #determineMove(T)}.
*
* @param game the current game
* @return the chosen move
*/
public final long getMove(T game) {
return determineMove(game.deepCopy());
}
/**
* Determines the player's move using a safe copy of the game.
* <p>
* This method is called by {@link #getMove(T)} and should contain
* the player's strategy for choosing a move.
*
* @param gameCopy a deep copy of the game
* @return the chosen move
*/
protected abstract long determineMove(T gameCopy);
/**
* Returns the player's name.
*
* @return the name
*/
public String getName() {
return this.name;
}
}

View File

@@ -2,6 +2,6 @@ package org.toop.framework.gameFramework.model.player;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
public interface MoveProvider<T extends TurnBasedGame> {
int getMove(T game);
public interface MoveProvider<T extends TurnBasedGame<T>> {
long getMove(T game);
}

View File

@@ -1,6 +1,7 @@
package org.toop.framework.gameFramework.model.player;
import org.toop.framework.gameFramework.model.game.DeepCopyable;
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
public interface Player<T extends TurnBasedGame<T>> extends NameProvider, MoveProvider<T> {
public interface Player<T extends TurnBasedGame<T>> extends NameProvider, MoveProvider<T>, DeepCopyable<Player<T>> {
}

View File

@@ -10,10 +10,6 @@ import org.toop.framework.eventbus.events.GenericEvent;
* player actions, and game completion.
*/
public class GUIEvents extends EventsBase {
/** Event to refresh or redraw the game canvas. */
public record RefreshGameCanvas() implements GenericEvent {}
/**
* Event indicating the game has ended.
*
@@ -23,8 +19,8 @@ public class GUIEvents extends EventsBase {
public record GameEnded(boolean winOrTie, int winner) implements GenericEvent {}
/** Event indicating a player has attempted a move. */
public record PlayerAttemptedMove(int move) implements GenericEvent {}
public record PlayerAttemptedMove(long move) implements GenericEvent {}
/** Event indicating a player is hovering over a move (for UI feedback). */
public record PlayerMoveHovered(int move) implements GenericEvent {}
public record PlayerMoveHovered(long move) implements GenericEvent {}
}