wip: prepare ai for using action trees and boards with score calculation

This commit is contained in:
punchready 2021-06-05 05:19:07 +02:00
parent aa1d2c48df
commit c97ff03a68
9 changed files with 338 additions and 18 deletions

View File

@ -1,7 +1,8 @@
package uulm.teamname.marvelous.gamelibrary.ai; package uulm.teamname.marvelous.gamelibrary.ai;
import uulm.teamname.marvelous.gamelibrary.ArrayTools; import uulm.teamname.marvelous.gamelibrary.config.CharacterConfig;
import uulm.teamname.marvelous.gamelibrary.IntVector2; 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.Character;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID; import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType; import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
@ -11,7 +12,6 @@ import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
import uulm.teamname.marvelous.gamelibrary.requests.RequestType; import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
/** Represents an AI instance for calculations. */ /** Represents an AI instance for calculations. */
class AI { class AI {
@ -21,10 +21,18 @@ class AI {
/** The Player the AI is playing for. */ /** The Player the AI is playing for. */
private final EntityType player; private final EntityType player;
/** The config data. */
private final PartyConfig partyConfig;
private final CharacterConfig characterConfig;
private final ScenarioConfig scenarioConfig;
/** Constructs a new {@link AI} playing the given player based on the given config values. */ /** Constructs a new {@link AI} playing the given player based on the given config values. */
public AI(GameStateView state, EntityType player) { public AI(GameStateView state, EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) {
this.state = state; this.state = state;
this.player = player; this.player = player;
this.partyConfig = partyConfig;
this.characterConfig = characterConfig;
this.scenarioConfig = scenarioConfig;
} }
/** /**
@ -33,28 +41,127 @@ class AI {
* @return A list of {@link Request Requests} with the actions the AI wants to perform * @return A list of {@link Request Requests} with the actions the AI wants to perform
*/ */
public ArrayList<Request> performTurn(EntityID turn) { public ArrayList<Request> performTurn(EntityID turn) {
System.out.println("[AI] Handling Character " + turn + " for Player " + player + ":");
ArrayList<Request> result = new ArrayList<>(); ArrayList<Request> result = new ArrayList<>();
Character character = (Character)state.getEntities().findEntity(turn); Character character = (Character)state.getEntities().findEntity(turn);
ArrayList<IntVector2> options = ArrayTools.toArrayList(IntVector2.CardinalDirections.clone()); Board board = Board.generate(state, player);
Collections.shuffle(options); Action action = board.analyze(character.getPosition(), player, partyConfig, characterConfig, scenarioConfig);
for(IntVector2 dir: options) { switch(action.type) {
IntVector2 target = character.getPosition().add(dir); case None -> {
if( System.out.println(" Result: doing nothing");
target.getX() < 0 || target.getX() >= state.getMapSize().getX() ||
target.getY() < 0 || target.getY() >= state.getMapSize().getY()
) {
continue;
} }
if(state.getEntities().findByPosition(target).isEmpty()) { case Move -> {
System.out.println(" Result: moving to " + action.target);
result.add(new RequestBuilder(RequestType.MoveRequest) result.add(new RequestBuilder(RequestType.MoveRequest)
.withOriginEntity(turn) .withOriginEntity(turn)
.withOriginField(character.getPosition()) .withOriginField(character.getPosition())
.withTargetField(character.getPosition().add(dir)) .withTargetField(action.target)
.buildCharacterRequest()); .buildCharacterRequest());
break; }
case MeleeAttack -> {
System.out.println(" Result: melee attacking " + action.targetEntity);
Character target = (Character)state.getEntities().findEntity(action.targetEntity);
if(target != null) {
result.add(new RequestBuilder(RequestType.MeleeAttackRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withValue(character.meleeDamage)
.buildCharacterRequest());
}
}
case RangedAttack -> {
System.out.println(" Result: ranged attacking " + action.targetEntity);
Character target = (Character)state.getEntities().findEntity(action.targetEntity);
if(target != null) {
result.add(new RequestBuilder(RequestType.RangedAttackRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withValue(character.rangedDamage)
.buildCharacterRequest());
}
}
case UseStone -> {
switch(action.stone) {
case SpaceStone -> {
System.out.println(" Result: using space stone to " + action.target);
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withStoneType(action.stone)
.buildCharacterRequest());
}
case MindStone -> {
System.out.println(" Result: using mind stone to " + action.target);
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withValue(partyConfig.mindStoneDMG)
.withStoneType(action.stone)
.buildCharacterRequest());
}
case RealityStone -> {
System.out.println(" Result: using reality stone at " + action.target);
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withStoneType(action.stone)
.buildCharacterRequest());
}
case PowerStone -> {
System.out.println(" Result: using power stone against " + action.targetEntity);
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withValue(character.rangedDamage * 2)
.withStoneType(action.stone)
.buildCharacterRequest());
}
case TimeStone -> {
System.out.println(" Result: using time stone");
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withOriginField(character.getPosition())
.withStoneType(action.stone)
.buildCharacterRequest());
}
case SoulStone -> {
System.out.println(" Result: using soul stone on " + action.targetEntity);
result.add(new RequestBuilder(RequestType.UseInfinityStoneRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withStoneType(action.stone)
.buildCharacterRequest());
}
}
}
case GiveStone -> {
System.out.println(" Result: giving stone to " + action.targetEntity);
Character target = (Character)state.getEntities().findEntity(action.targetEntity);
if(target != null) {
result.add(new RequestBuilder(RequestType.ExchangeInfinityStoneRequest)
.withOriginEntity(turn)
.withTargetEntity(action.targetEntity)
.withOriginField(character.getPosition())
.withTargetField(action.target)
.withStoneType(action.stone)
.buildCharacterRequest());
}
} }
} }

View File

@ -28,7 +28,7 @@ public class AIClient {
public AIClient(EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) { public AIClient(EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) {
this.game = new GameInstance(partyConfig, characterConfig, scenarioConfig); this.game = new GameInstance(partyConfig, characterConfig, scenarioConfig);
this.player = player; this.player = player;
this.ai = new AI(game.state, player); this.ai = new AI(game.state, player, partyConfig, characterConfig, scenarioConfig);
} }
/** /**

View File

@ -0,0 +1,47 @@
package uulm.teamname.marvelous.gamelibrary.ai;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.StoneType;
class Action {
public final ActionType type;
public final IntVector2 target;
public final EntityID targetEntity;
public final StoneType stone;
public Action(ActionType type) {
this.type = type;
this.target = null;
this.targetEntity = null;
this.stone = null;
}
public Action(ActionType type, IntVector2 target) {
this.type = type;
this.target = target;
this.targetEntity = null;
this.stone = null;
}
public Action(ActionType type, IntVector2 target, EntityID targetEntity) {
this.type = type;
this.target = target;
this.targetEntity = targetEntity;
this.stone = null;
}
public Action(ActionType type, StoneType stone) {
this.type = type;
this.target = null;
this.targetEntity = null;
this.stone = stone;
}
public Action(ActionType type, IntVector2 target, EntityID targetEntity, StoneType stone) {
this.type = type;
this.target = target;
this.targetEntity = targetEntity;
this.stone = stone;
}
}

View File

@ -0,0 +1,10 @@
package uulm.teamname.marvelous.gamelibrary.ai;
enum ActionType {
None,
Move,
MeleeAttack,
RangedAttack,
UseStone,
GiveStone
}

View File

@ -0,0 +1,73 @@
package uulm.teamname.marvelous.gamelibrary.ai;
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.*;
import uulm.teamname.marvelous.gamelibrary.entities.Character;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView;
import java.util.ArrayList;
class Board {
private final Piece[][] data;
private Board(Piece[][] data) {
this.data = data;
}
public static Board generate(GameStateView state, EntityType player) {
Piece[][] data = new Piece[state.getMapSize().getY()][state.getMapSize().getX()];
for(int x = 0; x < state.getMapSize().getX(); x++) {
for(int y = 0; y < state.getMapSize().getY(); y++) {
IntVector2 pos = new IntVector2(x, y);
ArrayList<Entity> entities = state.getEntities().findByPosition(pos);
if(entities.isEmpty()) {
data[y][x] = new Piece(PieceType.Empty);
}else {
Entity entity = entities.get(0);
switch(entity.id.type) {
case NPC -> {
if(entity.id.id == NPCType.Thanos.getID()) {
data[y][x] = new Piece(PieceType.Thanos);
}else {
data[y][x] = new Piece(PieceType.NPC);
}
}
case P1, P2 -> {
Character character = (Character)entity;
data[y][x] = new Piece(
entity.id.type == player ? PieceType.Friend : PieceType.Enemy,
entity.id,
character.hp.getValue(),
character.hp.getMax(),
character.mp.getValue(),
character.mp.getMax(),
character.ap.getValue(),
character.ap.getMax(),
character.inventory.getStonesAsArray()
);
}
case Rocks -> {
Rock rock = (Rock)entity;
data[y][x] = new Piece(PieceType.Rock, entity.id, rock.getHp(), rock.maxHP);
}
case InfinityStones -> {
InfinityStone stone = (InfinityStone)entity;
data[y][x] = new Piece(PieceType.InfinityStone, entity.id, stone.type);
}
case None -> {
data[y][x] = new Piece(PieceType.Empty);
}
}
}
}
}
return new Board(data);
}
public Action analyze(IntVector2 position, EntityType turn, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) {
return new Action(ActionType.None);
}
}

View File

@ -0,0 +1,60 @@
package uulm.teamname.marvelous.gamelibrary.ai;
import uulm.teamname.marvelous.gamelibrary.entities.*;
import java.util.Arrays;
import java.util.HashSet;
class Piece {
public final PieceType type;
public final EntityID id;
public final Stat hp;
public final Stat mp;
public final Stat ap;
public final HashSet<StoneType> inventory;
public final StoneType stone;
public Piece(PieceType type) {
this.type = type;
this.id = null;
this.hp = null;
this.mp = null;
this.ap = null;
this.inventory = null;
this.stone = null;
}
public Piece(PieceType type, EntityID id, int hp, int maxHP) {
this.type = type;
this.id = id;
this.hp = new Stat(StatType.HP, hp, maxHP);
this.mp = null;
this.ap = null;
this.inventory = null;
this.stone = null;
}
public Piece(PieceType type, EntityID id, StoneType stone) {
this.type = type;
this.id = id;
this.hp = null;
this.mp = null;
this.ap = null;
this.inventory = null;
this.stone = stone;
}
public Piece(PieceType type, EntityID id, int hp, int maxHP, int mp, int maxMP, int ap, int maxAP, StoneType[] inventory) {
this.type = type;
this.id = id;
this.hp = new Stat(StatType.HP, hp, maxHP);
this.mp = new Stat(StatType.MP, mp, maxMP);
this.ap = new Stat(StatType.AP, ap, maxAP);
this.inventory = new HashSet<>(Arrays.asList(inventory));
this.stone = null;
}
}

View File

@ -0,0 +1,11 @@
package uulm.teamname.marvelous.gamelibrary.ai;
enum PieceType {
Empty,
Rock,
Enemy,
Friend,
InfinityStone,
Thanos,
NPC
}

View File

@ -24,6 +24,18 @@ public class Stat {
this.value = max; this.value = max;
} }
/**
* Constructs a new {@link Stat} with the given values.
* @param type The {@link StatType} of the stat
* @param value The value of the stat
* @param max The maximum value of the stat
*/
public Stat(StatType type, int value, int max) {
this.type = type;
this.max = max;
this.value = value;
}
public int getValue() { public int getValue() {
return value; return value;
} }

View File

@ -39,7 +39,7 @@ class AITest extends BaseGameLogicTest {
void main() throws InterruptedException { void main() throws InterruptedException {
serverSend(server.startGame(player1Selection, player2Selection).toArray(new Event[0])); serverSend(server.startGame(player1Selection, player2Selection).toArray(new Event[0]));
Thread.sleep(10000); Thread.sleep(2000);
} }
private void clientSend(Request[] requests) { private void clientSend(Request[] requests) {