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;
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user