wip: implement score caltulation and action possibilities

This commit is contained in:
punchready 2021-06-06 02:00:12 +02:00
parent 98b47ea688
commit 598a1e00c1
4 changed files with 276 additions and 23 deletions

View File

@ -47,8 +47,8 @@ class AI {
Character character = (Character)state.getEntities().findEntity(turn);
BoardAnalyzer analyzer = new BoardAnalyzer(state, player);
Action action = analyzer.analyze(character.getPosition(), player, partyConfig, characterConfig, scenarioConfig);
BoardAnalyzer analyzer = new BoardAnalyzer(state, turn);
Action action = analyzer.analyze(state, character.getPosition(), turn);
switch(action.type) {
case None -> {

View File

@ -38,6 +38,13 @@ class Action {
this.stone = stone;
}
public Action(ActionType type, IntVector2 target, StoneType stone) {
this.type = type;
this.target = target;
this.targetEntity = null;
this.stone = stone;
}
public Action(ActionType type, IntVector2 target, EntityID targetEntity, StoneType stone) {
this.type = type;
this.target = target;

View File

@ -3,6 +3,7 @@ package uulm.teamname.marvelous.gamelibrary.ai;
import uulm.teamname.marvelous.gamelibrary.IntVector2;
import uulm.teamname.marvelous.gamelibrary.entities.*;
import uulm.teamname.marvelous.gamelibrary.entities.Character;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameLogic;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView;
import java.util.ArrayList;
@ -10,14 +11,16 @@ import java.util.Objects;
class Board {
private final Piece[][] data;
private final EntityType turn;
private final EntityType origin;
private final EntityID turn;
private Board(Piece[][] data, EntityType turn) {
private Board(Piece[][] data, EntityType origin, EntityID turn) {
this.data = data;
this.origin = origin;
this.turn = turn;
}
public static Board generate(GameStateView state, EntityType player) {
public static Board generate(GameStateView state, EntityID turn) {
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++) {
@ -64,18 +67,169 @@ class Board {
}
}
}
return new Board(data, player);
return new Board(data, turn.type, turn);
}
protected ArrayList<Action> generateActions() {
protected ArrayList<Action> generateActions(GameStateView state) {
ArrayList<Action> result = new ArrayList<>();
//TODO: generate possible actions
EntityType opposite = turn.type == EntityType.P1 ? EntityType.P2 : EntityType.P1;
Character character = (Character)state.getEntities().findEntity(turn);
Piece current = null;
IntVector2 position = null;
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
if(this.data[y][x].type == PieceType.Character && this.data[y][x].id.equals(turn)) {
current = this.data[y][x];
position = new IntVector2(x, y);
}
}
}
if(current == null) {
return result;
}
for(ActionType action: ActionType.values()) {
switch(action) {
case Move -> {
if(current.mp.getValue() <= 0) {
continue;
}
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
if(this.data[target.getY()][target.getX()].type == PieceType.Empty) {
result.add(new Action(ActionType.Move, target));
}
}
}
case MeleeAttack -> {
if(current.ap.getValue() <= 0) {
continue;
}
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
Piece targetPiece = this.data[target.getY()][target.getX()];
if(targetPiece.type == PieceType.Character && targetPiece.id.type == opposite) {
result.add(new Action(ActionType.MeleeAttack, target, targetPiece.id));
}
}
}
case RangedAttack -> {
if(current.ap.getValue() <= 0) {
continue;
}
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
IntVector2 pos = new IntVector2(x, y);
float dist = pos.distanceChebyshev(position);
if(this.data[y][x].type == PieceType.Character && this.data[y][x].id.type == opposite && dist > 1 && dist <= character.attackRange && lineOfSight(position, pos)) {
result.add(new Action(ActionType.RangedAttack, pos, this.data[y][x].id));
}
}
}
}
case UseStone -> {
if(current.ap.getValue() <= 0) {
continue;
}
for(StoneType stone: StoneType.values()) {
if(!character.inventory.hasStone(stone)) {
continue;
}
switch(stone) {
case SpaceStone -> {
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
if(this.data[y][x].type == PieceType.Empty) {
result.add(new Action(ActionType.UseStone, new IntVector2(x, y), stone));
}
}
}
}
case MindStone -> {
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
result.add(new Action(ActionType.UseStone, new IntVector2(x, y), stone));
}
}
}
case RealityStone -> {
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
Piece targetPiece = this.data[target.getY()][target.getX()];
if(targetPiece.type == PieceType.Empty) {
result.add(new Action(ActionType.UseStone, target, turn, stone));
}else if(targetPiece.type == PieceType.Rock) {
result.add(new Action(ActionType.UseStone, target, targetPiece.id, stone));
}
}
}
case PowerStone -> {
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
Piece targetPiece = this.data[target.getY()][target.getX()];
if(targetPiece.type == PieceType.Character && targetPiece.id.type == opposite) {
result.add(new Action(ActionType.UseStone, target, targetPiece.id, stone));
}
}
}
case TimeStone -> {
result.add(new Action(ActionType.UseStone, stone));
}
case SoulStone -> {
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
Piece targetPiece = this.data[target.getY()][target.getX()];
if(targetPiece.type == PieceType.Character && targetPiece.id.type == turn.type && targetPiece.hp.getValue() <= 0) {
result.add(new Action(ActionType.UseStone, target, targetPiece.id, stone));
}
}
}
}
}
}
case GiveStone -> {
if(current.ap.getValue() <= 0) {
continue;
}
for(StoneType stone: current.inventory) {
for(IntVector2 dir: IntVector2.CardinalDirections) {
IntVector2 target = position.add(dir);
if(target.getX() < 0 || target.getX() >= state.getMapSize().getX() || target.getY() < 0 || target.getY() >= state.getMapSize().getY()) {
continue;
}
Piece targetPiece = this.data[target.getY()][target.getX()];
if(targetPiece.type == PieceType.Character && targetPiece.id.type == turn.type) {
result.add(new Action(ActionType.GiveStone, target, targetPiece.id, stone));
}
}
}
}
}
}
return result;
}
protected Board applyAction(Action action) {
protected Board applyAction(GameStateView state, Action action) {
Piece[][] clone = new Piece[this.data.length][this.data[0].length];
for(int x = 0; x < this.data[0].length; x++) {
@ -84,17 +238,111 @@ class Board {
}
}
//TODO: apply action
Character character = (Character)state.getEntities().findEntity(turn);
return new Board(clone, turn == EntityType.P1 ? EntityType.P2 : EntityType.P1);
Piece current = null;
IntVector2 position = null;
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
if(this.data[y][x].type == PieceType.Character && this.data[y][x].id.equals(turn)) {
current = this.data[y][x];
position = new IntVector2(x, y);
}
}
}
//TODO: finalize action applying
switch(action.type) {
case Move -> {
clone[position.getY()][position.getX()] = clone[action.target.getY()][action.target.getX()];
clone[action.target.getY()][action.target.getX()] = current;
current.mp.decreaseValue(1);
}
case MeleeAttack -> {
clone[action.target.getY()][action.target.getX()].hp.decreaseValue(character.meleeDamage);
current.ap.decreaseValue(1);
}
case RangedAttack -> {
clone[action.target.getY()][action.target.getX()].hp.decreaseValue(character.meleeDamage);
current.ap.decreaseValue(1);
}
case UseStone -> {
switch(action.stone) {
case SpaceStone -> {
}
case MindStone -> {
}
case RealityStone -> {
}
case PowerStone -> {
}
case TimeStone -> {
}
case SoulStone -> {
}
}
}
case GiveStone -> {
}
}
//...get next turn in turn order...
//TODO: figure out next turn properly
return new Board(clone, origin, turn);
}
protected int calculateScore() {
//TODO: create score calculation
return 0;
protected float calculateScore() {
float score = 0;
for(int x = 0; x < this.data[0].length; x++) {
for(int y = 0; y < this.data.length; y++) {
IntVector2 current = new IntVector2(x, y);
Piece piece = this.data[y][x];
switch(piece.type) {
case Empty, Rock, NPC, InfinityStone, Thanos -> {
score += 0;
}
case Character -> {
float nearby = 0;
for(int x2 = 0; x2 < this.data[0].length; x2++) {
for(int y2 = 0; y2 < this.data.length; y2++) {
if(this.data[y2][x2].type == PieceType.InfinityStone) {
nearby += (1f / new IntVector2(x2, y2).distance(current)) * 3f;
}
}
}
float result = nearby + piece.inventory.size() + ((float)piece.hp.getValue() / (float)piece.hp.getMax()) * 2f;
if(piece.id.type == origin) {
score += result;
}else {
score -= result;
}
}
}
}
}
return score;
}
protected int calculateHash() {
return Objects.hash(super.hashCode(), data);
}
private boolean lineOfSight(IntVector2 a, IntVector2 b) {
for(IntVector2 pos: GameLogic.rasterize(a, b, false, false)) {
if(this.data[pos.getY()][pos.getX()].type != PieceType.Empty) {
return false;
}
}
return true;
}
}

View File

@ -1,9 +1,7 @@
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.EntityID;
import uulm.teamname.marvelous.gamelibrary.entities.EntityType;
import uulm.teamname.marvelous.gamelibrary.gamelogic.GameStateView;
@ -13,15 +11,15 @@ import java.util.HashMap;
class BoardAnalyzer {
private final Board origin;
private final EntityType player;
private final HashMap<Integer, Integer> cache = new HashMap<>();
private final HashMap<Integer, Float> cache = new HashMap<>();
public BoardAnalyzer(GameStateView state, EntityType player) {
this.origin = Board.generate(state, player);
this.player = player;
public BoardAnalyzer(GameStateView state, EntityID turn) {
this.origin = Board.generate(state, turn);
this.player = turn.type;
}
public Action analyze(IntVector2 position, EntityType turn, PartyConfig partyConfig, CharacterConfig characterConfig, ScenarioConfig scenarioConfig) {
ArrayList<Action> actions = origin.generateActions();
public Action analyze(GameStateView state, IntVector2 position, EntityID turn) {
ArrayList<Action> actions = origin.generateActions(state);
//TODO: create minimax tree