From 0f7109d41aeef5d9e6eb9bafc7ba8ad5cc5919f9 Mon Sep 17 00:00:00 2001 From: punchready Date: Thu, 29 Apr 2021 19:15:29 +0200 Subject: [PATCH] wip: backbone for the game logic package --- .../teamname/marvelous/gamelibrary/Tuple.java | 15 +++ .../marvelous/gamelibrary/events/Event.java | 9 ++ .../gamelogic/ChecksumCalculator.java | 23 +++++ .../gamelibrary/gamelogic/EventEmitter.java | 9 ++ .../gamelibrary/gamelogic/GameInstance.java | 91 +++++++++++++++++++ .../gamelibrary/gamelogic/GameLogic.java | 85 +++++++++++++++++ .../gamelibrary/gamelogic/GameState.java | 65 +++++++++++++ .../gamelogic/GameStateManager.java | 71 +++++++++++++++ .../gamelibrary/gamelogic/GameStateView.java | 26 ++++++ .../gamelogic/ParticipantType.java | 10 ++ .../gamelibrary/gamelogic/WinCondition.java | 15 +++ .../gamelibrary/json/MessageStructure.java | 2 + .../gamelibrary/requests/Request.java | 9 ++ 13 files changed, 430 insertions(+) create mode 100644 src/uulm/teamname/marvelous/gamelibrary/Tuple.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/events/Event.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/ChecksumCalculator.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/EventEmitter.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameState.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateView.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/ParticipantType.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/gamelogic/WinCondition.java create mode 100644 src/uulm/teamname/marvelous/gamelibrary/requests/Request.java diff --git a/src/uulm/teamname/marvelous/gamelibrary/Tuple.java b/src/uulm/teamname/marvelous/gamelibrary/Tuple.java new file mode 100644 index 0000000..b679e90 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/Tuple.java @@ -0,0 +1,15 @@ +package uulm.teamname.marvelous.gamelibrary; + +/** Represents a pair of two objects. + */ +public class Tuple { + public final X item1; + public final Y item2; + + /** Constructs a new {@link Tuple} based on the given objects. + */ + public Tuple(X item1, Y item2) { + this.item1 = item1; + this.item2 = item2; + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/events/Event.java b/src/uulm/teamname/marvelous/gamelibrary/events/Event.java new file mode 100644 index 0000000..a493812 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/events/Event.java @@ -0,0 +1,9 @@ +package uulm.teamname.marvelous.gamelibrary.events; + +import uulm.teamname.marvelous.gamelibrary.json.MessageStructure; + +/** Represents an abstract event sent inside a {@link MessageStructure} between client and server. + */ +public abstract class Event { + //TODO: implement Event +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ChecksumCalculator.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ChecksumCalculator.java new file mode 100644 index 0000000..9c441dd --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ChecksumCalculator.java @@ -0,0 +1,23 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +/** Contains checksum calculations. + */ +class ChecksumCalculator { + /** Compares a checksum to the checksum of a {@link GameState}. + * @param state The state to check. + * @param input the checksum to compare to. + * @return Whether or not the checksum matches the state's checksum. + */ + public static boolean checkChecksum(GameState state, long input) { + return calculateChecksum(state) == input; + } + + /** Calculates the corresponding checksum to a {@link GameState}. + * @param state The state to check. + * @return The checksum. + */ + public static long calculateChecksum(GameState state) { + //TODO: implement ChecksumCalculator.calculateChecksum + return 0; + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/EventEmitter.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/EventEmitter.java new file mode 100644 index 0000000..98b31f5 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/EventEmitter.java @@ -0,0 +1,9 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import java.util.Observable; + +/** Represents an event emitter for game events fired by a game instance. + */ +class EventEmitter extends Observable { + +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java new file mode 100644 index 0000000..700ae9e --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameInstance.java @@ -0,0 +1,91 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import uulm.teamname.marvelous.gamelibrary.IntVector2; +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.requests.Request; + +import java.util.Observer; + +/** Represents a game instance. + */ +public class GameInstance { + /** The private {@link GameState} of the instance. + */ + private final GameState _state; + + /** The public view for the underlying {@link GameState} of the instance. + */ + public final GameStateView state; + + /** The {@link GameStateManager} managing the {@link GameState} of the instance. + */ + private final GameStateManager manager; + + /** The {@link EventEmitter} for {@link Event}s resulting from {@link Request}s. + */ + private final EventEmitter emitter = new EventEmitter(); + + /** Constructs a new {@link GameInstance}. + */ + public GameInstance(IntVector2 mapSize) { + _state = new GameState(mapSize); + this.state = new GameStateView(_state); + manager = new GameStateManager(_state); + } + + /** Checks a checksum with the current one. + * @param input The checksum to compare to. + * @return Whether or not the checksum matches. + */ + public boolean checkChecksum(long input) { + return ChecksumCalculator.checkChecksum(_state, input); + } + + /** Calculates the current checksum of the game state. + * @return The calculated checksum. + */ + public long calculateChecksum() { + return ChecksumCalculator.calculateChecksum(_state); + } + + /** Checks an array of {@link Request}s for validity and automatically applies them if valid. + * @param requests The requests to check. + * @return Whether or not the given set of requests was valid. + */ + public boolean checkRequestsAndApply(Request... requests) { + if(manager.processRequests(requests, true)) { + emit(manager.apply()); + return true; + } + return false; + } + + /** Checks an array of {@link Request}s for validity without applying it. + * @param requests The requests to check. + * @return Whether or not the given set of requests is valid. + */ + public boolean checkRequestsSilent(Request... requests) { + return manager.processRequests(requests, false); + } + + /** Applies an array of {@link Event}s to the game state. + * @param events The events to apply. + */ + public void applyEvents(Event... events) { + manager.applyEvents(events); + } + + /** Adds an {@link Observer} for events. + * @param observer The observer to add. + */ + public void addObserver(Observer observer) { + emitter.addObserver(observer); + } + + /** Emits an array of {@link Event}s. + * @param events The events to emit. + */ + private void emit(Event... events) { + emitter.notifyObservers(events); + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java new file mode 100644 index 0000000..9528860 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameLogic.java @@ -0,0 +1,85 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import uulm.teamname.marvelous.gamelibrary.Tuple; +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.requests.Request; + +import java.util.ArrayList; +import java.util.HashMap; + +/** Contains game logic handling. + */ +class GameLogic { + /** Produces resulting {@link Event}s from a given {@link Request} independently of any {@link GameState}. + * @return The list of resulting events. + */ + public static ArrayList executeRequest(Request request) { + ArrayList result = new ArrayList(); + + //TODO: implement GameLogic.executeRequest + + /* Example Code: + switch (event.type) { + case EventType.MeleeAttack: + result.add(Event.Construct(EventType.APReduced, event.sourceEntity, event.ap)); + result.add(Event.Construct(EventType.DamageTaken, event.targetEntity, event.value)); + break; + } + */ + + return result; + } + + /** Checks a {@link Request} for validity for a {@link GameState}. + * @param state The game state to check on. + * @param request The request to validate. + * @return Whether or not the request is valid + */ + public static boolean checkRequest(GameState state, Request request) { + //TODO: implement GameLogic.checkRequest + return false; + } + + /** Applies an {@link Event} to a {@link GameState}. + * @param state The game state to apply to. + * @param event The event to apply. + */ + public static void applyEvent(GameState state, Event event) { + //TODO: implement GameLogic.applyEvent + } + + /** Checks a {@link GameState} for the current overtime win condition. + * @param state The game state to check. + * @return The {@link ParticipantType} that is currently winning the game according to overtime ruling. + */ + public static ParticipantType checkWinConditions(GameState state) { + //TODO: GameLogic.checkWinConditions is kind of ugly + + Tuple player1; + Tuple player2; + int value1; + int value2; + for(WinCondition condition: WinCondition.values()) { + player1 = new Tuple(ParticipantType.Player1, condition); + player2 = new Tuple(ParticipantType.Player2, condition); + value1 = 0; + value2 = 0; + + if(state.winConditions.containsKey(player1)) { + value1 = state.winConditions.get(player1); + } + if(state.winConditions.containsKey(player2)) { + value2 = state.winConditions.get(player2); + } + + if(value1 > value2) { + return ParticipantType.Player1; + } + if(value2 > value1) { + return ParticipantType.Player2; + } + } + + return ParticipantType.None; + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameState.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameState.java new file mode 100644 index 0000000..c70ea64 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameState.java @@ -0,0 +1,65 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import uulm.teamname.marvelous.gamelibrary.IntVector2; +import uulm.teamname.marvelous.gamelibrary.entities.Entity; +import uulm.teamname.marvelous.gamelibrary.entities.EntityID; +import uulm.teamname.marvelous.gamelibrary.entities.StoneType; +import uulm.teamname.marvelous.gamelibrary.Tuple; + +import java.util.ArrayList; +import java.util.HashMap; + +/** Represents the state of a game instance. + */ +class GameState { + /** The size of the map. + */ + public final IntVector2 mapSize; + + /** The list of {@link Entity}'s inside the game. + */ + public ArrayList entities; + + /** The total amount of full turn cycles that occurred. + */ + public int roundNumber = 0; + + /** The turn order of every character. + */ + public ArrayList turnOrder; + + /** The total amount of turns that occurred. + */ + public int turnNumber = 0; + + /** The {@link EntityID} of the active character. + */ + public EntityID activeCharacter; + + /** Whether or not the game was won. + */ + public boolean won = false; + + /** The global cooldown of every infinity stone. + */ + public HashMap stoneCooldown; + + /** The store of the {@link WinCondition} data for every win condition for each player. + */ + public HashMap, Integer> winConditions; + + /** Constructs a new {@link GameState}. + * @param mapSize The size of the map. + */ + public GameState(IntVector2 mapSize) { + this.mapSize = mapSize; + } + + /** Clones the state into a new {@link GameState} object. + * @return The cloned game state. + */ + public GameState snapshot() { + //TODO: implement GameState.snapshot + return this; + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java new file mode 100644 index 0000000..19ce7aa --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateManager.java @@ -0,0 +1,71 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.requests.Request; + +import java.util.ArrayDeque; +import java.util.ArrayList; + +/** Represents manager for a game state. + */ +class GameStateManager { + /** The managed {@link GameState}. + */ + private final GameState state; + + /** The queue of {@link Event}s to be applied during {@link Request} processing. + */ + private final ArrayDeque queue = new ArrayDeque(); + + /** Constructs a new {@link GameStateManager}. + * @param state A reference to the state to be managed. + */ + public GameStateManager(GameState state) { + this.state = state; + } + + /** Checks a list of {@link Request}s for validity and optionally produces resulting {@link Event}s. + * @param requests The requests to check. + * @param apply True if resulting events should be stored for later application. + */ + public boolean processRequests(Request[] requests, boolean apply) { + GameState snapshot = state.snapshot(); + + for(Request request: requests) { + if (GameLogic.checkRequest(snapshot, request)) { + ArrayList result = GameLogic.executeRequest(request); + if(apply) { + queue.addAll(result); + } + }else { + queue.clear(); + return false; + } + } + + return true; + } + + /** Applies an array of {@link Event}s to the game state. + * @param events The events to apply. + */ + public void applyEvents(Event[] events) { + for(Event event: events) { + GameLogic.applyEvent(state, event); + } + } + + /** Applies the result of the last processRequests call. + * @return A list of applied events. + */ + public Event[] apply() { + Event[] toReturn = new Event[queue.size()]; + Event current; + int i = 0; + while ((current = queue.poll()) != null) { + GameLogic.applyEvent(state, current); + toReturn[i++] = current; + } + return toReturn; + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateView.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateView.java new file mode 100644 index 0000000..e805f3d --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/GameStateView.java @@ -0,0 +1,26 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +import uulm.teamname.marvelous.gamelibrary.entities.Entity; + +import java.util.ArrayList; + +/** Represents a game state view containing getters for all the properties of a {@link GameState}. + */ +public class GameStateView { + /** The managed {@link GameState}. + */ + private final GameState state; + + /** Constructs a new {@link GameStateView}. + * @param state A reference to the state to be viewable. + */ + public GameStateView(GameState state) { + this.state = state; + } + + //TODO: add immutable getters for all state properties + + public ArrayList getEntities() { + return new ArrayList<>(state.entities); + } +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ParticipantType.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ParticipantType.java new file mode 100644 index 0000000..f192fdd --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/ParticipantType.java @@ -0,0 +1,10 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +/** Specifies a participant type. + */ +public enum ParticipantType { + None, + Player1, + Player2, + Spectator +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/gamelogic/WinCondition.java b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/WinCondition.java new file mode 100644 index 0000000..86f3a48 --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/gamelogic/WinCondition.java @@ -0,0 +1,15 @@ +package uulm.teamname.marvelous.gamelibrary.gamelogic; + +/** Specifies a win condition. The order is important here. + */ +enum WinCondition { + /** The maximum amount of total infinity stones the player had at a time. + */ + MaxStones, + /** The total amount of enemy characters the player knocked out. + */ + TotalKnockouts, + /** The total amount of damage the player did to enemy characters. + */ + TotalDamage +} diff --git a/src/uulm/teamname/marvelous/gamelibrary/json/MessageStructure.java b/src/uulm/teamname/marvelous/gamelibrary/json/MessageStructure.java index 5b0e8c4..1438edd 100644 --- a/src/uulm/teamname/marvelous/gamelibrary/json/MessageStructure.java +++ b/src/uulm/teamname/marvelous/gamelibrary/json/MessageStructure.java @@ -7,6 +7,8 @@ import java.util.HashMap; /** Represents a message sent between client and server and contains all possible data. */ public class MessageStructure { + //TODO: revise MessageStructure according to actual standard document + /** The list of {@link Event}s sent inside the message. */ public Event[] events; diff --git a/src/uulm/teamname/marvelous/gamelibrary/requests/Request.java b/src/uulm/teamname/marvelous/gamelibrary/requests/Request.java new file mode 100644 index 0000000..24bea5c --- /dev/null +++ b/src/uulm/teamname/marvelous/gamelibrary/requests/Request.java @@ -0,0 +1,9 @@ +package uulm.teamname.marvelous.gamelibrary.requests; + +import uulm.teamname.marvelous.gamelibrary.json.MessageStructure; + +/** Represents an abstract request sent inside a {@link MessageStructure} between client and server. + */ +public abstract class Request { + //TODO: implement Request +}