Skip to content

feat(parser): max-attackers-each-combat static (Judoon Enforcers + class)#3709

Open
ntindle wants to merge 1 commit into
phase-rs:mainfrom
ntindle:fix/who-misparse-31-cluster-31
Open

feat(parser): max-attackers-each-combat static (Judoon Enforcers + class)#3709
ntindle wants to merge 1 commit into
phase-rs:mainfrom
ntindle:fix/who-misparse-31-cluster-31

Conversation

@ntindle

@ntindle ntindle commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the "no more than N creatures can attack [you] each combat" static as MaxAttackersEachCombat { max, defender } — a general class-level primitive (defender=Controller for "attack you", defender=null for the global form).

Cards supported

Judoon Enforcers,Crawlspace Astral Arena,Caverns of Despair Dueling Grounds,Silent Arbiter

From the WHO Suspend/time cluster (Judoon Enforcers had this clause alongside Trample + Suspend 6, both preserved); the primitive generalizes to several non-WHO cards.

Verification

cargo fmt clean - cargo clippy -p engine --all-targets -D warnings clean - cargo test -p engine all pass - oracle-gen AST confirmed for all affected cards.

CR refs verified against docs/MagicCompRules.txt.

🤖 Generated with Claude Code

@ntindle ntindle requested a review from matthewevans as a code owner June 18, 2026 07:18

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements defender-scoped attacker caps (such as "no more than one creature can attack you each combat") by updating StaticMode::MaxAttackersEachCombat to support an optional defender scope and introducing validation logic to enforce these limits per defending player. The feedback recommends expanding the unit tests to explicitly verify that defender-scoped caps do not restrict attacks against other, unprotected players in multiplayer scenarios.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +3425 to +3433
// Two attackers against player 1: illegal.
assert!(validate_per_defender_attacker_caps(
&state,
&[
(a, AttackTarget::Player(PlayerId(1))),
(b, AttackTarget::Player(PlayerId(1))),
],
)
.is_err());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

[MEDIUM] Expand test coverage to verify defender-scoped restriction in multiplayer scenarios.

Why it matters: The test currently only verifies that attacking the protected player (Player 1) is restricted, but it does not assert that attacking other players (e.g., Player 2) remains unrestricted by the defender-scoped cap. Adding this assertion ensures that the cap is correctly scoped to the defender and does not act as a global cap in multiplayer games.

Suggested fix: Add an assertion verifying that attacking an unprotected player is legal.

Suggested change
// Two attackers against player 1: illegal.
assert!(validate_per_defender_attacker_caps(
&state,
&[
(a, AttackTarget::Player(PlayerId(1))),
(b, AttackTarget::Player(PlayerId(1))),
],
)
.is_err());
// Two attackers against player 1: illegal.
assert!(validate_per_defender_attacker_caps(
&state,
&[
(a, AttackTarget::Player(PlayerId(1))),
(b, AttackTarget::Player(PlayerId(1))),
],
)
.is_err());
// Two attackers against player 2 (unprotected): legal.
assert!(validate_per_defender_attacker_caps(
&state,
&[
(a, AttackTarget::Player(PlayerId(2))),
(b, AttackTarget::Player(PlayerId(2))),
],
)
.is_ok());

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: request-changes

[HIGH] Defender-scoped caps treat attacks on planeswalkers/battles as attacks "you". Evidence: crates/engine/src/game/combat.rs:551 counts every attack whose defending_player_for_target equals the static controller, and crates/engine/src/game/combat.rs:567 maps planeswalker targets to their controller and battle targets to their protector. The parser only creates this scoped mode for the plain text can attack you each combat, while the existing attack-scope machinery treats plain AttackTargetFilter::Player as direct-player only and reserves PlayerOrPlaneswalker for explicit you or planeswalkers you control text (crates/engine/src/game/restrictions.rs:1837, crates/engine/src/parser/oracle_static/shared.rs:2993).

Why this matters: Judoon Enforcers/Crawlspace-style text would reject legal declarations such as two creatures attacking a planeswalker controlled by the protected player, or a battle they protect, even though those creatures are not attacking that player. Please count against a direct-player defended scope here (or reuse the existing target-scope matcher) and add regression coverage for planeswalker/battle targets plus the multiplayer non-protected-player case Gemini noted.

Facts/confidence: confirmed from this diff and the existing parser/runtime attack-target scope code; high confidence. The evidence that would contradict this is a CR/card-text rule saying plain "attack you" includes planeswalkers or battles, but CR 508.1b/508.5 and the current engine model distinguish those attack targets.

@matthewevans matthewevans added the enhancement New feature or request label Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants