AI: improved game stability on blocking (related to #13290)

This commit is contained in:
Oleg Agafonov 2025-02-04 10:21:13 +04:00
parent 8aa432067a
commit 7035736a6c
3 changed files with 18 additions and 20 deletions

View file

@ -15,11 +15,7 @@ public class CombatInfo {
private Map<Permanent, List<Permanent>> combat = new HashMap<>(); private Map<Permanent, List<Permanent>> combat = new HashMap<>();
public void addPair(Permanent attacker, Permanent blocker) { public void addPair(Permanent attacker, Permanent blocker) {
List<Permanent> blockers = combat.get(attacker); List<Permanent> blockers = combat.computeIfAbsent(attacker, k -> new ArrayList<>());
if (blockers == null) {
blockers = new ArrayList<>();
combat.put(attacker, blockers);
}
blockers.add(blocker); blockers.add(blocker);
} }

View file

@ -179,15 +179,15 @@ public final class CombatUtil {
nonBlockingDiffScore.put(s.getBlocker(), s.getDiffNonblockingScore()); nonBlockingDiffScore.put(s.getBlocker(), s.getDiffNonblockingScore());
}); });
// split blockers usage priority // split blockers by usage priority
List<Permanent> survivedAndKillBlocker = new ArrayList<>(); List<Permanent> survivedAndKillBlocker = new ArrayList<>();
List<Permanent> survivedBlockers2 = new ArrayList<>(); List<Permanent> survivedBlockers = new ArrayList<>();
List<Permanent> diedBlockers = new ArrayList<>(); List<Permanent> diedBlockers = new ArrayList<>();
blockerStats.forEach(stats -> { blockerStats.forEach(stats -> {
if (stats.isAttackerDied() && !stats.isBlockerDied()) { if (stats.isAttackerDied() && !stats.isBlockerDied()) {
survivedAndKillBlocker.add(stats.getBlocker()); survivedAndKillBlocker.add(stats.getBlocker());
} else if (!stats.isBlockerDied()) { } else if (!stats.isBlockerDied()) {
survivedBlockers2.add(stats.getBlocker()); survivedBlockers.add(stats.getBlocker());
} else { } else {
diedBlockers.add(stats.getBlocker()); diedBlockers.add(stats.getBlocker());
} }
@ -196,10 +196,10 @@ public final class CombatUtil {
int blockedCount = 0; int blockedCount = 0;
// find good blocker // find good blocker
Permanent blocker = getWorstCreature(survivedAndKillBlocker, survivedBlockers2); Permanent blocker = getWorstCreature(survivedAndKillBlocker, survivedBlockers);
if (blocker != null) { if (blocker != null) {
combatInfo.addPair(attacker, blocker); combatInfo.addPair(attacker, blocker);
removeWorstCreature(blocker, blockers, survivedAndKillBlocker, survivedBlockers2); removeWorstCreature(blocker, blockers, survivedAndKillBlocker, survivedBlockers);
blockedCount++; blockedCount++;
} }
@ -208,15 +208,17 @@ public final class CombatUtil {
// TODO: there are many triggers on damage, attack, etc - it can't be processed without real game simulations // TODO: there are many triggers on damage, attack, etc - it can't be processed without real game simulations
if (blocker == null) { if (blocker == null) {
blocker = getWorstCreature(diedBlockers); blocker = getWorstCreature(diedBlockers);
if (blocker != null) {
int diffBlockingScore = blockingDiffScore.getOrDefault(blocker, 0); int diffBlockingScore = blockingDiffScore.getOrDefault(blocker, 0);
int diffNonBlockingScore = nonBlockingDiffScore.getOrDefault(blocker, 0); int diffNonBlockingScore = nonBlockingDiffScore.getOrDefault(blocker, 0);
if (diffBlockingScore >= 0 || diffBlockingScore > diffNonBlockingScore) { if (diffBlockingScore >= 0 || diffBlockingScore > diffNonBlockingScore) {
// it's good use case - can block and kill // it's good - can sacrifice and get better game state, also protect from game loose
combatInfo.addPair(attacker, blocker); combatInfo.addPair(attacker, blocker);
removeWorstCreature(blocker, blockers, diedBlockers); removeWorstCreature(blocker, blockers, diedBlockers);
blockedCount++; blockedCount++;
} }
} }
}
// find blockers for restrictions // find blockers for restrictions
while (true) { while (true) {
@ -230,10 +232,10 @@ public final class CombatUtil {
// effects support: can't be blocked except by xxx or more creatures // effects support: can't be blocked except by xxx or more creatures
if (blockedCount > 0 && attacker.getMinBlockedBy() > blockedCount) { if (blockedCount > 0 && attacker.getMinBlockedBy() > blockedCount) {
// it already has 1 blocker (killer in best use case), so no needs in second killer // it already has 1 blocker (killer in best use case), so no needs in second killer
blocker = getWorstCreature(survivedBlockers2, survivedAndKillBlocker, diedBlockers); blocker = getWorstCreature(survivedBlockers, survivedAndKillBlocker, diedBlockers);
if (blocker != null) { if (blocker != null) {
combatInfo.addPair(attacker, blocker); combatInfo.addPair(attacker, blocker);
removeWorstCreature(blocker, blockers, survivedBlockers2, survivedAndKillBlocker, diedBlockers); removeWorstCreature(blocker, blockers, survivedBlockers, survivedAndKillBlocker, diedBlockers);
blockedCount++; blockedCount++;
continue; // try to find next required blocker continue; // try to find next required blocker
} else { } else {

View file

@ -822,7 +822,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
// too few blockers // too few blockers
if (attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) { if (attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) {
for (UUID blockerId : new ArrayList<>(blockers)) { for (UUID blockerId : new ArrayList<>(blockers)) {
game.getCombat().removeBlocker(blockerId, game); // ! game.getCombat().removeBlocker(blockerId, game);
} }
blockers.clear(); blockers.clear();
blockerOrder.clear(); blockerOrder.clear();