package com.cardshifter.ai.phrancis;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.cardshifter.modapi.actions.ECSAction;
import com.cardshifter.modapi.actions.TargetSet;
import com.cardshifter.modapi.base.ComponentRetriever;
import com.cardshifter.modapi.base.Entity;
import com.cardshifter.modapi.base.PlayerComponent;
import com.cardshifter.modapi.base.Retrievers;
import com.cardshifter.modapi.cards.BattlefieldComponent;
import com.cardshifter.modapi.cards.CardComponent;
import com.cardshifter.modapi.cards.HandComponent;
import com.cardshifter.modapi.cards.ZoneComponent;
import com.cardshifter.modapi.resources.ResourceRetriever;
import net.zomis.aiscores.ScoreParameters;
import net.zomis.cardshifter.ecs.usage.CyborgChroniclesGame;
import net.zomis.cardshifter.ecs.usage.CyborgChroniclesGame.CyborgChroniclesResources;
public class AttackAnalyze {
private static final ResourceRetriever health = ResourceRetriever.forResource(CyborgChroniclesResources.HEALTH);
private static final ResourceRetriever attack = ResourceRetriever.forResource(CyborgChroniclesResources.ATTACK);
private static final ResourceRetriever scrapCost = ResourceRetriever.forResource(CyborgChroniclesResources.SCRAP_COST);
public static double attackScore(ECSAction action, ScoreParameters<Entity> params) {
if (!action.getName().equals(CyborgChroniclesGame.ATTACK_ACTION)) {
return 0;
}
TargetSet targets = action.getTargetSets().get(0);
targets.clearTargets();
List<Entity> possibleTargets = targets.findPossibleTargets();
Optional<Entity> player = possibleTargets.stream().filter(e -> e.hasComponent(PlayerComponent.class)).findAny();
if (player.isPresent()) {
// attacking a player is really the best option there is
targets.addTarget(player.get());
return 100;
}
if (possibleTargets.isEmpty()) {
throw new RuntimeException("Attack action has no targets: " + action);
}
possibleTargets.sort(Comparator.comparingInt(e -> attackVS(action.getOwner(), e)));
Entity chosenTarget = possibleTargets.get(possibleTargets.size() - 1);
targets.addTarget(chosenTarget);
return attackVS(action.getOwner(), chosenTarget);
}
private static int attackVS(Entity attacker, Entity target) {
int score = 0;
boolean attackerDies = attack.getFor(target) >= health.getFor(attacker);
boolean targetDies = attack.getFor(attacker) >= health.getFor(target);
int trampleDamage = attack.getFor(attacker) - health.getFor(target);
int additionalDamageBack = attack.getFor(target) - health.getFor(attacker);
if (attackerDies && !targetDies) {
// this is really the worst option there is
return -10;
}
if (targetDies) {
score += 100;
score -= trampleDamage;
}
if (attackerDies) {
score -= 97;
score += additionalDamageBack;
}
return score;
}
public static double scrapNeeded(ECSAction action, ScoreParameters<Entity> params) {
if (!action.getName().equals(CyborgChroniclesGame.SCRAP_ACTION)) {
return 0;
}
Entity entity = action.getOwner();
ComponentRetriever<CardComponent> card = Retrievers.component(CardComponent.class);
Entity owner = card.get(entity).getOwner();
HandComponent hand = owner.getComponent(HandComponent.class);
return hand.stream().mapToInt(e -> scrapCost.getOrDefault(e, 0)).sum();
}
public static double scrapIfCanGetKilled(ECSAction action, ScoreParameters<Entity> params) {
if (!action.getName().equals(CyborgChroniclesGame.SCRAP_ACTION)) {
return 0;
}
Entity entity = action.getOwner();
ComponentRetriever<CardComponent> card = Retrievers.component(CardComponent.class);
Entity owner = card.get(entity).getOwner();
Set<Entity> players = entity.getGame().getEntitiesWithComponent(PlayerComponent.class);
Entity opponent = players.stream().filter(pl -> pl.getComponent(PlayerComponent.class).getIndex() != owner.getComponent(PlayerComponent.class).getIndex()).findAny().get();
ZoneComponent battlefield = opponent.getComponent(BattlefieldComponent.class);
int myHealth = health.getFor(entity);
int myAttack = attack.getFor(entity);
List<Entity> creaturesCanKill = battlefield.stream().filter(e -> attack.getOrDefault(e, 0) >= myHealth).collect(Collectors.toList());
if (creaturesCanKill.isEmpty()) {
// This creature cannot die from any opponent creature, then it is safe
return -10;
}
Stream<Entity> creaturesCanNotDie = creaturesCanKill.stream().filter(e -> health.getOrDefault(e, 0) > myAttack);
if (creaturesCanNotDie.findAny().isPresent()) {
// If I stay here, then I am toast.
return 1;
}
// We can kill each other
return 0.25;
}
public static double scrapScore(ECSAction action, ScoreParameters<Entity> params) {
if (!action.getName().equals(CyborgChroniclesGame.SCRAP_ACTION)) {
return 0;
}
Entity entity = action.getOwner();
ComponentRetriever<CardComponent> card = Retrievers.component(CardComponent.class);
ZoneComponent battlefield = card.get(entity).getCurrentZone();
List<Entity> creatures = battlefield.getCards();
if (creatures.size() <= 3) {
return -health.getFor(entity);
}
creatures.sort(Comparator.comparingInt(e -> health.getFor(e) + attack.getFor(e)));
if (entity == creatures.get(0)) {
// Only consider scrapping the creature with lowest health
return 4 - health.getFor(entity);
}
return -1;
}
public static double health(ECSAction action, ScoreParameters<Entity> params) {
return health.getOrDefault(action.getOwner(), 0);
}
public static double attack(ECSAction action, ScoreParameters<Entity> params) {
return attack.getOrDefault(action.getOwner(), 0);
}
public static double enchantScore(ECSAction action, ScoreParameters<Entity> params) {
if (!action.getName().equals(CyborgChroniclesGame.ENCHANT_ACTION)) {
return 0;
}
TargetSet targets = action.getTargetSets().get(0);
targets.clearTargets();
List<Entity> possibleTargets = targets.findPossibleTargets();
if (possibleTargets.isEmpty()) {
return -1;
}
Entity enchantment = action.getOwner();
int attackBonus = attack.getFor(enchantment);
int healthBonus = health.getFor(enchantment);
possibleTargets.sort(Comparator.comparingDouble(e -> enchantScore(e, attackBonus, healthBonus)));
Entity chosenTarget = possibleTargets.get(possibleTargets.size() - 1);
targets.addTarget(chosenTarget);
return enchantScore(chosenTarget, attackBonus, healthBonus);
}
private static double enchantScore(Entity e, int attackBonus, int healthBonus) {
return 1.5*(health.getFor(e) + healthBonus) + attack.getFor(e) + attackBonus;
}
}