mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Merge new framework into development (#269)
* Created a somewhat generic TurnBasedGame thread. Temporary UI that only works for TicTacToe rn. Added a LocalPlayer with the intent to add more players
* (RANDOM COMMIT) Hope it works
* Changes by bas
* Fixed dependency issues
* Fixed major issue in game deepcopy
* Merge conflict fix
* Removed unused import
* Update GTBGT branch from dev branch (#263)
* started a basis for the tutorials, tic tac toe is almost done with some general stuff still to do.
* rest van de tutorials toegevoegd
* Removed views
* Merge conflict fix
* Removed unused import
---------
Co-authored-by: michiel301b <m.brands.3@st.hanze.nl>
Co-authored-by: ramollia <>
Co-authored-by: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com>
* Revert "Update GTBGT branch from dev branch (#263)"
This reverts commit 9134d7e343.
* Fixed frontend not using GameController because of spaghetti code.
* Removed unused imports
* GameCanvas not implements a DrawPlayerMove that can be overridden for specific implementations
* Created an event that will request the controller to refresh the UI.
* ADDED DEPENDENCY. Renamed GameControllers to GameManagers, gameThread is not game controller.
* Attempt at adding an online player. I think it doesn't work because of unsubscriben after success not working
* Multiplayer is functional through OnlineThreadBehaviour. Empty slots are currently represented by -1 in the GUI.
* Removed sout spam, added logger than I can't get to work.
* Idek what these changes are
* Te lang geen commit, sorry
* Multiplayer seems to work pretty well now, hopefully I can add the other games soon.
* Added unsubscribe to EventFlow. ListenerHandler now functional. GlobalEventbus now user listenerHandler
* getAllListeners
* Removed nulls
* Inbetween commit of adding Reversi. This is a lot of spaghetti.
* Fixed stress tests
* Fixed typo in NetworkingGameClientHandler that prevented losses from being received
* Missed 2nd typo. Fixed
* Added docs, no more list creation when adding events to the bus.
* Fixed unsubscribe not working.
* Moved away from deprecated functions
* moved from wildcard to typed
* Moved away from deprecated function
* Added debugging to GlobalEventBus
* Fixed cleaning flow
* Fixed unsubscribe all
* Fixed unsubscribe all
* Removed unused import
* Works now with updated EventFlow(). Unsubscribing works. ReversiAIR has an issue where a forced move returns -1 and local play back button doesn't work properly. To be fixed
* Fixed ReversiR issue that caused skip turn desync
* Fixed color mismatch with server and online main player is now correct.
* Added a bunch of java doc and small changes
* Small changes
* Added a new Thread Behaviour to test framework.
* Fixed human error I made in TicTacToeR logic...
* Fixed broken event and wrong player being presented as winner.
* Idk changes
* Fixed PR conflicts
---------
Co-authored-by: michiel301b <m.brands.3@st.hanze.nl>
Co-authored-by: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package org.toop.framework.gameFramework;
|
||||
|
||||
import org.toop.framework.eventbus.events.EventsBase;
|
||||
import org.toop.framework.eventbus.events.GenericEvent;
|
||||
|
||||
/**
|
||||
* Defines GUI-related events for the event bus.
|
||||
* <p>
|
||||
* These events notify the UI about updates such as game progress,
|
||||
* 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.
|
||||
*
|
||||
* @param winOrTie true if the game ended in a win, false for a draw
|
||||
* @param winner the index of the winning player, or -1 if no winner
|
||||
*/
|
||||
public record GameEnded(boolean winOrTie, int winner) implements GenericEvent {}
|
||||
|
||||
/** Event indicating a player has attempted a move. */
|
||||
public record PlayerAttemptedMove(int move) implements GenericEvent {}
|
||||
|
||||
/** Event indicating a player is hovering over a move (for UI feedback). */
|
||||
public record PlayerMoveHovered(int move) implements GenericEvent {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.toop.framework.gameFramework;
|
||||
|
||||
/**
|
||||
* Represents the current state of a turn-based game.
|
||||
*/
|
||||
public enum GameState {
|
||||
/** Game is ongoing and no special condition applies. */
|
||||
NORMAL,
|
||||
|
||||
/** Game ended in a draw. */
|
||||
DRAW,
|
||||
|
||||
/** Game ended with a win for a player. */
|
||||
WIN,
|
||||
|
||||
/** Next player's turn was skipped. */
|
||||
TURN_SKIPPED,
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.toop.framework.gameFramework;
|
||||
|
||||
import org.toop.framework.gameFramework.GameState;
|
||||
|
||||
/**
|
||||
* Represents the result of a move in a turn-based game.
|
||||
*
|
||||
* @param state the resulting {@link GameState} after the move
|
||||
* @param player the index of the player associated with the result (winner or relevant player)
|
||||
*/
|
||||
public record PlayResult(GameState state, int player) {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.toop.framework.gameFramework.abstractClasses;
|
||||
|
||||
import org.toop.framework.gameFramework.interfaces.IAIMoveR;
|
||||
|
||||
/**
|
||||
* Abstract base class for AI implementations for games extending {@link GameR}.
|
||||
* <p>
|
||||
* Provides a common superclass for specific AI algorithms. Concrete subclasses
|
||||
* must implement the {@link #findBestMove(GameR, int)} method defined by
|
||||
* {@link IAIMoveR} to determine the best move given a game state and a search depth.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the specific type of game this AI can play, extending {@link GameR}
|
||||
*/
|
||||
public abstract class AIR<T extends GameR> implements IAIMoveR<T> {
|
||||
// Concrete AI implementations should override findBestMove(T game, int depth)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.toop.framework.gameFramework.abstractClasses;
|
||||
|
||||
import org.toop.framework.gameFramework.interfaces.IPlayableR;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Abstract base class representing a general grid-based game.
|
||||
* <p>
|
||||
* Provides the basic structure for games with a two-dimensional board stored as a
|
||||
* one-dimensional array. Tracks the board state, row and column sizes, and provides
|
||||
* helper methods for accessing and modifying the board.
|
||||
* </p>
|
||||
* <p>
|
||||
* Concrete subclasses must implement the {@link #clone()} method and can extend this
|
||||
* class with specific game rules, winning conditions, and move validation logic.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class GameR implements IPlayableR, Cloneable {
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
* Constructs a new game board with the specified row and column size.
|
||||
*
|
||||
* @param rowSize number of rows (> 0)
|
||||
* @param columnSize number of columns (> 0)
|
||||
* @throws AssertionError if rowSize or columnSize is not positive
|
||||
*/
|
||||
|
||||
protected GameR(int rowSize, int columnSize) {
|
||||
assert rowSize > 0 && columnSize > 0;
|
||||
|
||||
this.rowSize = rowSize;
|
||||
this.columnSize = columnSize;
|
||||
|
||||
board = new int[rowSize * columnSize];
|
||||
Arrays.fill(board, EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for creating a deep copy of another game instance.
|
||||
*
|
||||
* @param copy the game instance to copy
|
||||
*/
|
||||
protected GameR(GameR copy) {
|
||||
this.rowSize = copy.rowSize;
|
||||
this.columnSize = copy.columnSize;
|
||||
this.board = copy.board.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an array contains a value.
|
||||
*
|
||||
* @param array array containing ints
|
||||
* @param value int to check for
|
||||
*
|
||||
* @return true if array contains value
|
||||
*/
|
||||
|
||||
public static boolean contains(int[] array, int value) {
|
||||
// O(n)
|
||||
for (int element : array){
|
||||
if (element == value) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a specific position on the board.
|
||||
*
|
||||
* @param position the index in the board array
|
||||
* @param player the value to set (e.g., player number)
|
||||
*/
|
||||
protected void setBoardPosition(int position, int player) {
|
||||
this.board[position] = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a deep copy of this game instance.
|
||||
* <p>
|
||||
* Subclasses must implement this method to ensure proper copying of any
|
||||
* additional fields beyond the base board structure.
|
||||
* </p>
|
||||
*
|
||||
* @return a cloned instance of this game
|
||||
*/
|
||||
@Override
|
||||
public abstract GameR clone();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.toop.framework.gameFramework.abstractClasses;
|
||||
|
||||
public abstract class TurnBasedGameR extends GameR {
|
||||
private final int playerCount; // How many players are playing
|
||||
private int turn = 0; // What turn it is in the game
|
||||
|
||||
protected TurnBasedGameR(int rowSize, int columnSize, int playerCount) {
|
||||
super(rowSize, columnSize);
|
||||
this.playerCount = playerCount;
|
||||
}
|
||||
|
||||
protected TurnBasedGameR(TurnBasedGameR other){
|
||||
super(other);
|
||||
this.playerCount = other.playerCount;
|
||||
this.turn = other.turn;
|
||||
}
|
||||
|
||||
public int getPlayerCount(){return this.playerCount;}
|
||||
|
||||
protected void nextTurn() {
|
||||
turn += 1;
|
||||
}
|
||||
|
||||
public int getCurrentTurn() {
|
||||
return turn % playerCount;
|
||||
}
|
||||
|
||||
protected void setBoard(int position) {
|
||||
super.setBoardPosition(position, getCurrentTurn());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.toop.framework.gameFramework.interfaces;
|
||||
|
||||
import org.toop.framework.gameFramework.abstractClasses.GameR;
|
||||
|
||||
/**
|
||||
* AI interface for selecting the best move in a game.
|
||||
*
|
||||
* @param <T> the type of game this AI can play, extending {@link GameR}
|
||||
*/
|
||||
public interface IAIMoveR<T extends GameR> {
|
||||
|
||||
/**
|
||||
* Determines the optimal move for the current player.
|
||||
*
|
||||
* @param game the current game state
|
||||
* @param depth the search depth for evaluating moves
|
||||
* @return an integer representing the chosen move
|
||||
*/
|
||||
int findBestMove(T game, int depth);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.toop.framework.gameFramework.interfaces;
|
||||
|
||||
import org.toop.framework.gameFramework.GameState;
|
||||
import org.toop.framework.gameFramework.PlayResult;
|
||||
|
||||
/**
|
||||
* Interface for turn-based games that can be played and queried for legal moves.
|
||||
*/
|
||||
public interface IPlayableR {
|
||||
|
||||
/**
|
||||
* Returns the moves that are currently valid in the game.
|
||||
*
|
||||
* @return an array of integers representing legal moves
|
||||
*/
|
||||
int[] getLegalMoves();
|
||||
|
||||
/**
|
||||
* Plays the given move and returns the resulting game state.
|
||||
*
|
||||
* @param move the move to apply
|
||||
* @return the {@link GameState} and additional info after the move
|
||||
*/
|
||||
PlayResult play(int move);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.toop.framework.gameFramework.interfaces;
|
||||
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
|
||||
/**
|
||||
* Interface for games that support online multiplayer play.
|
||||
* <p>
|
||||
* Methods are called in response to network events from the server.
|
||||
*/
|
||||
public interface SupportsOnlinePlay {
|
||||
|
||||
/** Called when it is this player's turn to make a move. */
|
||||
void yourTurn(NetworkEvents.YourTurnResponse event);
|
||||
|
||||
/** Called when a move from another player is received. */
|
||||
void moveReceived(NetworkEvents.GameMoveResponse event);
|
||||
|
||||
/** Called when the game has finished, with the final result. */
|
||||
void gameFinished(NetworkEvents.GameResultResponse event);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.toop.framework.gameFramework.interfaces;
|
||||
|
||||
/**
|
||||
* Interface for classes that can trigger a UI update.
|
||||
*/
|
||||
public interface UpdatesGameUI {
|
||||
|
||||
/** Called to refresh or update the game UI. */
|
||||
void updateUI();
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package org.toop.framework.networking.handlers;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -70,7 +72,7 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
case "CHALLENGE":
|
||||
gameChallengeHandler(recSrvRemoved);
|
||||
return;
|
||||
case "WIN", "DRAW", "LOSE":
|
||||
case "WIN", "DRAW", "LOSS":
|
||||
gameWinConditionHandler(recSrvRemoved);
|
||||
return;
|
||||
default:
|
||||
@@ -119,13 +121,12 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
private void gameWinConditionHandler(String rec) {
|
||||
@SuppressWarnings("StreamToString")
|
||||
String condition =
|
||||
Pattern.compile("\\b(win|draw|lose)\\b", Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.toString()
|
||||
.trim();
|
||||
String condition = Pattern.compile("\\b(win|draw|loss)\\b", Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(MatchResult::group)
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.GameResultResponse(this.connectionId, condition))
|
||||
|
||||
Reference in New Issue
Block a user