From e90975b620f159fe00c9f3cb6e2eaa85e7acd036 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 18 Jun 2026 20:34:01 +0200 Subject: [PATCH 1/3] test(engine): lock chained explore resume after DigChoice (#1151) Regression coverage for Jadelight Ranger's double explore: the second explore must stay stashed until the first explore's nonland dig choice resolves. Co-authored-by: Cursor --- crates/engine/src/game/effects/explore.rs | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/crates/engine/src/game/effects/explore.rs b/crates/engine/src/game/effects/explore.rs index 30431965f9..6b26d0b276 100644 --- a/crates/engine/src/game/effects/explore.rs +++ b/crates/engine/src/game/effects/explore.rs @@ -946,4 +946,81 @@ mod tests { other => panic!("expected ExploreChoice, got {other:?}"), } } + + /// CR 701.44a (issue #1151): Jadelight Ranger explores twice — the second + /// explore must resume after the first explore's nonland DigChoice completes. + #[test] + fn chained_explore_resumes_after_nonland_dig_choice() { + use crate::game::engine; + use crate::types::actions::GameAction; + use crate::types::zones::Zone; + + let mut state = GameState::new_two_player(42); + let ranger = create_object( + &mut state, + CardId(1), + PlayerId(0), + "Jadelight Ranger".to_string(), + Zone::Battlefield, + ); + state + .objects + .get_mut(&ranger) + .unwrap() + .card_types + .core_types + .push(CoreType::Creature); + + let bolt_a = create_object( + &mut state, + CardId(2), + PlayerId(0), + "Lightning Bolt".to_string(), + Zone::Library, + ); + let bolt_b = create_object( + &mut state, + CardId(3), + PlayerId(0), + "Shock".to_string(), + Zone::Library, + ); + state.players[0].library = vec![bolt_a, bolt_b].into(); + + let second_explore = ResolvedAbility::new(Effect::Explore, vec![], ranger, PlayerId(0)); + let ability = ResolvedAbility::new(Effect::Explore, vec![], ranger, PlayerId(0)) + .sub_ability(second_explore); + let mut events = Vec::new(); + resolve_ability_chain(&mut state, &ability, &mut events, 0).unwrap(); + + assert!( + matches!(state.waiting_for, WaitingFor::DigChoice { .. }), + "first explore should pause on DigChoice, got {:?}", + state.waiting_for + ); + assert!( + state.pending_continuation.is_some(), + "second explore must be stashed while first explore waits for DigChoice" + ); + assert_eq!( + state.objects[&ranger].counters.get(&CounterType::Plus1Plus1).copied(), + Some(1), + "first explore should add one +1/+1 counter" + ); + + let WaitingFor::DigChoice { cards, .. } = state.waiting_for.clone() else { + unreachable!(); + }; + engine::apply_as_current( + &mut state, + GameAction::SelectCards { cards: vec![cards[0]] }, + ) + .unwrap(); + + assert_eq!( + state.objects[&ranger].counters.get(&CounterType::Plus1Plus1).copied(), + Some(2), + "second explore should add another +1/+1 counter after DigChoice resolves" + ); + } } From ea76fc02249ac59432545731f04d9bcde0f3e593 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 18 Jun 2026 21:06:59 +0200 Subject: [PATCH 2/3] style(engine): format explore regression test for CI Co-authored-by: Cursor --- crates/engine/src/game/effects/explore.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/game/effects/explore.rs b/crates/engine/src/game/effects/explore.rs index 6b26d0b276..7a7f499b11 100644 --- a/crates/engine/src/game/effects/explore.rs +++ b/crates/engine/src/game/effects/explore.rs @@ -1003,7 +1003,10 @@ mod tests { "second explore must be stashed while first explore waits for DigChoice" ); assert_eq!( - state.objects[&ranger].counters.get(&CounterType::Plus1Plus1).copied(), + state.objects[&ranger] + .counters + .get(&CounterType::Plus1Plus1) + .copied(), Some(1), "first explore should add one +1/+1 counter" ); @@ -1013,12 +1016,17 @@ mod tests { }; engine::apply_as_current( &mut state, - GameAction::SelectCards { cards: vec![cards[0]] }, + GameAction::SelectCards { + cards: vec![cards[0]], + }, ) .unwrap(); assert_eq!( - state.objects[&ranger].counters.get(&CounterType::Plus1Plus1).copied(), + state.objects[&ranger] + .counters + .get(&CounterType::Plus1Plus1) + .copied(), Some(2), "second explore should add another +1/+1 counter after DigChoice resolves" ); From 9686b12c885e117da62ab8953fba7ef862a796b1 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Thu, 18 Jun 2026 12:58:39 -0700 Subject: [PATCH 3/3] style(engine): move explore test imports to module scope --- crates/engine/src/game/effects/explore.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/game/effects/explore.rs b/crates/engine/src/game/effects/explore.rs index 7a7f499b11..c1489c3248 100644 --- a/crates/engine/src/game/effects/explore.rs +++ b/crates/engine/src/game/effects/explore.rs @@ -394,11 +394,13 @@ pub fn handle_choice( #[cfg(test)] mod tests { use super::*; + use crate::game::engine; use crate::game::zones::create_object; use crate::types::ability::{ AbilityDefinition, AbilityKind, ControllerRef, Effect, QuantityExpr, ReplacementDefinition, TargetFilter, TargetRef, TypedFilter, }; + use crate::types::actions::GameAction; use crate::types::identifiers::{CardId, ObjectId}; use crate::types::keywords::Keyword; use crate::types::player::PlayerId; @@ -951,10 +953,6 @@ mod tests { /// explore must resume after the first explore's nonland DigChoice completes. #[test] fn chained_explore_resumes_after_nonland_dig_choice() { - use crate::game::engine; - use crate::types::actions::GameAction; - use crate::types::zones::Zone; - let mut state = GameState::new_two_player(42); let ranger = create_object( &mut state,