From cffd8d81098ce1e58a72aa39246ad657aa10dc12 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Sun, 22 Mar 2026 04:52:28 +0000 Subject: [PATCH] fix: add creature damage accumulation to combat engine Previously _resolve_combat() used a binary power-vs-toughness kill check: creatures only died if a single source had power >= their toughness in one swing. This meant a 2/2 wouldn't die to two 1/1 tokens blocking it, and repeated smaller hits across combats never accumulated. Changes: - models.py: add damage_marked field to Card (default 0) - engine.py: accumulate damage via damage_marked += power instead of binary a_pow >= toughness checks in _resolve_combat() - engine.py: clear damage_marked = 0 in untap phase (MTG rule 514.2) This enables multi-blocker lethality, incremental chip damage within a combat step, and proper creature death-by-accumulated-damage interactions. --- src/commander_ai_lab/sim/engine.py | 8 ++++++-- src/commander_ai_lab/sim/models.py | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/commander_ai_lab/sim/engine.py b/src/commander_ai_lab/sim/engine.py index 63404ad..616c1d1 100644 --- a/src/commander_ai_lab/sim/engine.py +++ b/src/commander_ai_lab/sim/engine.py @@ -127,6 +127,7 @@ class GameEngine: # ── Untap (correct MTG order: untap is first) ── for c in sim.get_battlefield(pi): c.tapped = False + c.damage_marked = 0 # clear damage each turn (MTG rule 514.2) # ── Draw ── if p.library: @@ -590,12 +591,15 @@ class GameEngine: blocked = True if events is not None: combat_details.append(f"{atk.name} blocked by {blocker.name}") - if a_pow >= blocker.get_toughness() or atk.has_keyword("deathtouch"): + # Accumulate damage on creatures (not binary kill) + blocker.damage_marked += a_pow + if blocker.damage_marked >= blocker.get_toughness() or atk.has_keyword("deathtouch"): sim.remove_from_battlefield(blocker.id) self._send_to_graveyard(sim, blocker, opp_idx) if events is not None: combat_details.append(f" {blocker.name} dies") - if b_pow >= a_tou or blocker.has_keyword("deathtouch"): + atk.damage_marked += b_pow + if atk.damage_marked >= a_tou or blocker.has_keyword("deathtouch"): sim.remove_from_battlefield(atk.id) self._send_to_graveyard(sim, atk, pi) if events is not None: diff --git a/src/commander_ai_lab/sim/models.py b/src/commander_ai_lab/sim/models.py index e155dc9..30b2069 100644 --- a/src/commander_ai_lab/sim/models.py +++ b/src/commander_ai_lab/sim/models.py @@ -43,6 +43,7 @@ class Card: owner_id: int = -1 tapped: bool = False turn_played: int = -1 + damage_marked: int = 0 # track damage marked on creature (cleared each cleanup step) def get_power(self) -> int: """Extract numeric power from pt string."""