From aa1d2c48df10cb6b20d85216cedb6d630c5adf27 Mon Sep 17 00:00:00 2001 From: punchready Date: Sat, 5 Jun 2021 04:19:58 +0200 Subject: [PATCH] refactor: split ai into client and "brain" --- .../teamname/marvelous/gamelibrary/ai/AI.java | 61 ++++++------------ .../marvelous/gamelibrary/ai/AIClient.java | 64 +++++++++++++++++++ .../gamelibrary/gamelogic/AITest.java | 10 +-- 3 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AIClient.java diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AI.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AI.java index c9a83b7..db4dc5b 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AI.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AI.java @@ -2,61 +2,40 @@ package uulm.teamname.marvelous.gamelibrary.ai; import uulm.teamname.marvelous.gamelibrary.ArrayTools; import uulm.teamname.marvelous.gamelibrary.IntVector2; -import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig; -import uulm.teamname.marvelous.gamelibrary.config.PartyConfig; -import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig; import uulm.teamname.marvelous.gamelibrary.entities.Character; import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityType; -import uulm.teamname.marvelous.gamelibrary.events.Event; -import uulm.teamname.marvelous.gamelibrary.events.EventType; -import uulm.teamname.marvelous.gamelibrary.gamelogic.*; +import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView; import uulm.teamname.marvelous.gamelibrary.requests.Request; import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder; import uulm.teamname.marvelous.gamelibrary.requests.RequestType; import java.util.ArrayList; import java.util.Collections; -import java.util.List; -import java.util.Optional; -public class AI { - private final GameInstance game; +/** Represents an AI instance for calculations. */ +class AI { + /** The {@link GameStateView} the AI is playing on. */ + private final GameStateView state; + + /** The Player the AI is playing for. */ private final EntityType player; - public AI(EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) { - this.game = new GameInstance(partyConfig, characterConfig, scenarioConfig); + /** Constructs a new {@link AI} playing the given player based on the given config values. */ + public AI(GameStateView state, EntityType player) { + this.state = state; this.player = player; } - public Optional> handle(Event... events) { + /** + * Calculates the appropriate actions for a turn of any of the characters the AI is playing for. + * @param turn The {@link EntityID} that currently has the turn + * @return A list of {@link Request Requests} with the actions the AI wants to perform + */ + public ArrayList performTurn(EntityID turn) { ArrayList result = new ArrayList<>(); - game.applyEvents(events); - - boolean containsTurn = false; - for(Event event: events) { - if(event.type == EventType.TurnEvent) { - containsTurn = true; - break; - } - } - - if(containsTurn && game.state.getActiveCharacter() != null && game.state.getActiveCharacter().type == player) { - result.addAll(handleTurn(game.state.getActiveCharacter())); - } - - if(!result.isEmpty()) { - return Optional.of(result); - }else { - return Optional.empty(); - } - } - - private ArrayList handleTurn(EntityID turn) { - ArrayList result = new ArrayList<>(); - - Character character = (Character)game.state.getEntities().findEntity(turn); + Character character = (Character)state.getEntities().findEntity(turn); ArrayList options = ArrayTools.toArrayList(IntVector2.CardinalDirections.clone()); Collections.shuffle(options); @@ -64,12 +43,12 @@ public class AI { for(IntVector2 dir: options) { IntVector2 target = character.getPosition().add(dir); if( - target.getX() < 0 || target.getX() >= game.state.getMapSize().getX() || - target.getY() < 0 || target.getY() >= game.state.getMapSize().getY() + target.getX() < 0 || target.getX() >= state.getMapSize().getX() || + target.getY() < 0 || target.getY() >= state.getMapSize().getY() ) { continue; } - if(game.state.getEntities().findByPosition(target).isEmpty()) { + if(state.getEntities().findByPosition(target).isEmpty()) { result.add(new RequestBuilder(RequestType.MoveRequest) .withOriginEntity(turn) .withOriginField(character.getPosition()) diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AIClient.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AIClient.java new file mode 100644 index 0000000..b2da49f --- /dev/null +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/ai/AIClient.java @@ -0,0 +1,64 @@ +package uulm.teamname.marvelous.gamelibrary.ai; + +import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig; +import uulm.teamname.marvelous.gamelibrary.config.PartyConfig; +import uulm.teamname.marvelous.gamelibrary.config.ScenarioConfig; +import uulm.teamname.marvelous.gamelibrary.entities.EntityType; +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.events.EventType; +import uulm.teamname.marvelous.gamelibrary.gamelogic.GameInstance; +import uulm.teamname.marvelous.gamelibrary.requests.Request; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** Represents an AI player. */ +public class AIClient { + /** The {@link GameInstance} the AI is playing on. */ + private final GameInstance game; + + /** The Player the AI is playing for. */ + private final EntityType player; + + /** The actual {@link AI} instance. */ + private final AI ai; + + /** Constructs a new {@link AIClient} playing the given player based on the given config values. */ + public AIClient(EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) { + this.game = new GameInstance(partyConfig, characterConfig, scenarioConfig); + this.player = player; + this.ai = new AI(game.state, player); + } + + /** + * Handles incoming {@link Event Events} and optionally returns a list of response {@link Request Requests}. + * @param events The incoming events + * @return Optionally resulting requests + */ + public Optional> handle(Event... events) { + ArrayList result = new ArrayList<>(); + + game.applyEvents(events); + + boolean containsTurn = false; + for(Event event: events) { + if(event.type == EventType.TurnEvent) { + containsTurn = true; + break; + } + } + + //detect turn + if(containsTurn && game.state.getActiveCharacter() != null && game.state.getActiveCharacter().type == player) { + //let ai calculate requests + result.addAll(this.ai.performTurn(game.state.getActiveCharacter())); + } + + if(!result.isEmpty()) { + return Optional.of(result); + }else { + return Optional.empty(); + } + } +} diff --git a/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/AITest.java b/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/AITest.java index 8d74a23..a67d7aa 100644 --- a/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/AITest.java +++ b/src/test/java/uulm/teamname/marvelous/gamelibrary/gamelogic/AITest.java @@ -2,7 +2,7 @@ package uulm.teamname.marvelous.gamelibrary.gamelogic; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import uulm.teamname.marvelous.gamelibrary.ai.AI; +import uulm.teamname.marvelous.gamelibrary.ai.AIClient; import uulm.teamname.marvelous.gamelibrary.entities.EntityType; import uulm.teamname.marvelous.gamelibrary.events.Event; import uulm.teamname.marvelous.gamelibrary.json.JSON; @@ -20,8 +20,8 @@ class AITest extends BaseGameLogicTest { private static ExecutorService executor; private static GameInstance server; - private static AI clientA; - private static AI clientB; + private static AIClient clientA; + private static AIClient clientB; @BeforeAll static void setUp() { @@ -31,8 +31,8 @@ class AITest extends BaseGameLogicTest { executor = new SimpleErrorSensitiveThreadPoolExecutor(); server = new GameInstance(partyConfig, characterConfig, scenarioConfig); - clientA = new AI(EntityType.P1, partyConfig, characterConfig, scenarioConfig); - clientB = new AI(EntityType.P2, partyConfig, characterConfig, scenarioConfig); + clientA = new AIClient(EntityType.P1, partyConfig, characterConfig, scenarioConfig); + clientB = new AIClient(EntityType.P2, partyConfig, characterConfig, scenarioConfig); } @Test