From 7035736a6c96bd4bcde60809c9ea3539f5b25525 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 4 Feb 2025 10:21:13 +0400 Subject: [PATCH] AI: improved game stability on blocking (related to #13290) --- .../src/mage/player/ai/util/CombatInfo.java | 6 +--- .../src/mage/player/ai/util/CombatUtil.java | 30 ++++++++++--------- .../java/mage/game/combat/CombatGroup.java | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatInfo.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatInfo.java index f77f38df7fe..3b80dc0a38b 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatInfo.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatInfo.java @@ -15,11 +15,7 @@ public class CombatInfo { private Map> combat = new HashMap<>(); public void addPair(Permanent attacker, Permanent blocker) { - List blockers = combat.get(attacker); - if (blockers == null) { - blockers = new ArrayList<>(); - combat.put(attacker, blockers); - } + List blockers = combat.computeIfAbsent(attacker, k -> new ArrayList<>()); blockers.add(blocker); } diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java index 03dd0c2690e..e727b28855a 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/util/CombatUtil.java @@ -179,15 +179,15 @@ public final class CombatUtil { nonBlockingDiffScore.put(s.getBlocker(), s.getDiffNonblockingScore()); }); - // split blockers usage priority + // split blockers by usage priority List survivedAndKillBlocker = new ArrayList<>(); - List survivedBlockers2 = new ArrayList<>(); + List survivedBlockers = new ArrayList<>(); List diedBlockers = new ArrayList<>(); blockerStats.forEach(stats -> { if (stats.isAttackerDied() && !stats.isBlockerDied()) { survivedAndKillBlocker.add(stats.getBlocker()); } else if (!stats.isBlockerDied()) { - survivedBlockers2.add(stats.getBlocker()); + survivedBlockers.add(stats.getBlocker()); } else { diedBlockers.add(stats.getBlocker()); } @@ -196,10 +196,10 @@ public final class CombatUtil { int blockedCount = 0; // find good blocker - Permanent blocker = getWorstCreature(survivedAndKillBlocker, survivedBlockers2); + Permanent blocker = getWorstCreature(survivedAndKillBlocker, survivedBlockers); if (blocker != null) { combatInfo.addPair(attacker, blocker); - removeWorstCreature(blocker, blockers, survivedAndKillBlocker, survivedBlockers2); + removeWorstCreature(blocker, blockers, survivedAndKillBlocker, survivedBlockers); blockedCount++; } @@ -208,13 +208,15 @@ public final class CombatUtil { // TODO: there are many triggers on damage, attack, etc - it can't be processed without real game simulations if (blocker == null) { blocker = getWorstCreature(diedBlockers); - int diffBlockingScore = blockingDiffScore.getOrDefault(blocker, 0); - int diffNonBlockingScore = nonBlockingDiffScore.getOrDefault(blocker, 0); - if (diffBlockingScore >= 0 || diffBlockingScore > diffNonBlockingScore) { - // it's good use case - can block and kill - combatInfo.addPair(attacker, blocker); - removeWorstCreature(blocker, blockers, diedBlockers); - blockedCount++; + if (blocker != null) { + int diffBlockingScore = blockingDiffScore.getOrDefault(blocker, 0); + int diffNonBlockingScore = nonBlockingDiffScore.getOrDefault(blocker, 0); + if (diffBlockingScore >= 0 || diffBlockingScore > diffNonBlockingScore) { + // it's good - can sacrifice and get better game state, also protect from game loose + combatInfo.addPair(attacker, blocker); + removeWorstCreature(blocker, blockers, diedBlockers); + blockedCount++; + } } } @@ -230,10 +232,10 @@ public final class CombatUtil { // effects support: can't be blocked except by xxx or more creatures if (blockedCount > 0 && attacker.getMinBlockedBy() > blockedCount) { // 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) { combatInfo.addPair(attacker, blocker); - removeWorstCreature(blocker, blockers, survivedBlockers2, survivedAndKillBlocker, diedBlockers); + removeWorstCreature(blocker, blockers, survivedBlockers, survivedAndKillBlocker, diedBlockers); blockedCount++; continue; // try to find next required blocker } else { diff --git a/Mage/src/main/java/mage/game/combat/CombatGroup.java b/Mage/src/main/java/mage/game/combat/CombatGroup.java index a86bb84d283..9021beb2b83 100644 --- a/Mage/src/main/java/mage/game/combat/CombatGroup.java +++ b/Mage/src/main/java/mage/game/combat/CombatGroup.java @@ -822,7 +822,7 @@ public class CombatGroup implements Serializable, Copyable { // too few blockers if (attacker.getMinBlockedBy() > 1 && !blockers.isEmpty() && blockers.size() < attacker.getMinBlockedBy()) { for (UUID blockerId : new ArrayList<>(blockers)) { - game.getCombat().removeBlocker(blockerId, game); // ! + game.getCombat().removeBlocker(blockerId, game); } blockers.clear(); blockerOrder.clear();