wip: prepare ai for using action trees and boards with score calculation
This commit is contained in:
parent
aa1d2c48df
commit
c97ff03a68
@ -1,7 +1,8 @@
|
||||
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;
|
||||
@ -11,7 +12,6 @@ import uulm.teamname.marvelous.gamelibrary.requests.RequestBuilder;
|
||||
import uulm.teamname.marvelous.gamelibrary.requests.RequestType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/** Represents an AI instance for calculations. */
|
||||
class AI {
|
||||
@ -21,10 +21,18 @@ class AI {
|
||||
/** The Player the AI is playing for. */
|
||||
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. */
|
||||
public AI(GameStateView state, EntityType player) {
|
||||
public AI(GameStateView state, EntityType player, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) {
|
||||
this.state = state;
|
||||
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
|
||||
*/
|
||||
public ArrayList<Request> performTurn(EntityID turn) {
|
||||
System.out.println("[AI] Handling Character " + turn + " for Player " + player + ":");
|
||||
|
||||
ArrayList<Request> result = new ArrayList<>();
|
||||
|
||||
Character character = (Character)state.getEntities().findEntity(turn);
|
||||
|
||||
ArrayList<IntVector2> options = ArrayTools.toArrayList(IntVector2.CardinalDirections.clone());
|
||||
Collections.shuffle(options);
|
||||
Board board = Board.generate(state, player);
|
||||
Action action = board.analyze(character.getPosition(), player, partyConfig, characterConfig, scenarioConfig);
|
||||
|
||||
for(IntVector2 dir: options) {
|
||||
IntVector2 target = character.getPosition().add(dir);
|
||||
if(
|
||||
target.getX() < 0 || target.getX() >= state.getMapSize().getX() ||
|
||||
target.getY() < 0 || target.getY() >= state.getMapSize().getY()
|
||||
) {
|
||||
continue;
|
||||
switch(action.type) {
|
||||
case None -> {
|
||||
System.out.println(" Result: doing nothing");
|
||||
}
|
||||
if(state.getEntities().findByPosition(target).isEmpty()) {
|
||||
case Move -> {
|
||||
System.out.println(" Result: moving to " + action.target);
|
||||
result.add(new RequestBuilder(RequestType.MoveRequest)
|
||||
.withOriginEntity(turn)
|
||||
.withOriginField(character.getPosition())
|
||||
.withTargetField(character.getPosition().add(dir))
|
||||
.withTargetField(action.target)
|
||||
.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ public class AIClient {
|
||||
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);
|
||||
this.ai = new AI(game.state, player, partyConfig, characterConfig, scenarioConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package uulm.teamname.marvelous.gamelibrary.ai;
|
||||
|
||||
enum ActionType {
|
||||
None,
|
||||
Move,
|
||||
MeleeAttack,
|
||||
RangedAttack,
|
||||
UseStone,
|
||||
GiveStone
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package uulm.teamname.marvelous.gamelibrary.ai;
|
||||
|
||||
enum PieceType {
|
||||
Empty,
|
||||
Rock,
|
||||
Enemy,
|
||||
Friend,
|
||||
InfinityStone,
|
||||
Thanos,
|
||||
NPC
|
||||
}
|
@ -24,6 +24,18 @@ public class Stat {
|
||||
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() {
|
||||
return value;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class AITest extends BaseGameLogicTest {
|
||||
void main() throws InterruptedException {
|
||||
serverSend(server.startGame(player1Selection, player2Selection).toArray(new Event[0]));
|
||||
|
||||
Thread.sleep(10000);
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
|
||||
private void clientSend(Request[] requests) {
|
||||
|
Loading…
Reference in New Issue
Block a user