refactor: split ai into client and "brain"

This commit is contained in:
punchready 2021-06-05 04:19:58 +02:00
parent 2cbd86b725
commit aa1d2c48df
3 changed files with 89 additions and 46 deletions

View File

@ -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<List<Request>> 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<Request> performTurn(EntityID turn) {
ArrayList<Request> 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<Request> handleTurn(EntityID turn) {
ArrayList<Request> result = new ArrayList<>();
Character character = (Character)game.state.getEntities().findEntity(turn);
Character character = (Character)state.getEntities().findEntity(turn);
ArrayList<IntVector2> 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())

View File

@ -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<List<Request>> handle(Event... events) {
ArrayList<Request> 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();
}
}
}

View File

@ -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