Interaction Testing
This document outlines the methodologies and tools used to test interactions between cards in Rummage. Interaction testing is crucial for ensuring that complex card combinations work correctly according to Magic: The Gathering rules.
Interaction Complexity
Card interactions in Magic: The Gathering can be extremely complex due to:
- Layering system: Effects apply in a specific order
- Timing rules: Effects happen at specific times
- Replacement effects: Effects that replace other effects
- Prevention effects: Effects that prevent other effects
- Triggered abilities: Abilities that trigger based on events
- State-based actions: Automatic game rules
Testing Approach
Interaction testing follows a systematic approach:
- Identify interaction pairs: Determine which cards interact
- Document expected behavior: Define how the interaction should work
- Create test scenarios: Design scenarios that test the interaction
- Execute tests: Run the tests and capture results
- Verify outcomes: Compare results to expected behavior
Interaction Test Framework
The interaction test framework provides tools for testing card interactions:
#![allow(unused)] fn main() { // Example of testing card interactions #[test] fn test_humility_and_opalescence_interaction() { let mut scenario = TestScenario::new(); // Set up players let player = scenario.add_player(20); // Add the interacting cards let humility = scenario.add_card_to_battlefield("Humility", player); let opalescence = scenario.add_card_to_battlefield("Opalescence", player); // Add a test enchantment let test_enchantment = scenario.add_card_to_battlefield("Pacifism", player); // Verify the interaction effects // 1. Check that Humility is a 4/4 creature with no abilities let humility_card = scenario.get_permanent(humility); assert_eq!(humility_card.power, 4); assert_eq!(humility_card.toughness, 4); assert!(humility_card.has_no_abilities()); // 2. Check that Opalescence is a 4/4 creature with no abilities let opalescence_card = scenario.get_permanent(opalescence); assert_eq!(opalescence_card.power, 4); assert_eq!(opalescence_card.toughness, 4); assert!(opalescence_card.has_no_abilities()); // 3. Check that Pacifism is a 4/4 creature with no abilities let pacifism_card = scenario.get_permanent(test_enchantment); assert_eq!(pacifism_card.power, 4); assert_eq!(pacifism_card.toughness, 4); assert!(pacifism_card.has_no_abilities()); // 4. Check that creatures on the battlefield are not affected by Pacifism let test_creature = scenario.add_card_to_battlefield("Grizzly Bears", player); let creature_card = scenario.get_permanent(test_creature); assert!(creature_card.can_attack()); } }
Interaction Categories
Different categories of interactions require specific testing approaches:
Layer Interactions
Testing interactions between effects in different layers:
#![allow(unused)] fn main() { #[test] fn test_layer_interactions() { let mut scenario = TestScenario::new(); let player = scenario.add_player(20); // Layer 4 (Type) and Layer 7 (P/T) interaction let bear = scenario.add_card_to_battlefield("Grizzly Bears", player); let mutation = scenario.add_card_to_hand("Artificial Evolution", player); let giant_growth = scenario.add_card_to_hand("Giant Growth", player); // Change creature type (Layer 4) scenario.play_card(mutation, Some(bear)); scenario.choose_text("Bear"); scenario.choose_text("Elephant"); scenario.resolve_top_of_stack(); // Boost power/toughness (Layer 7) scenario.play_card(giant_growth, Some(bear)); scenario.resolve_top_of_stack(); // Verify the creature is now a 5/5 Elephant let bear_card = scenario.get_permanent(bear); assert_eq!(bear_card.power, 5); assert_eq!(bear_card.toughness, 5); assert!(bear_card.type_line.contains("Elephant")); assert!(!bear_card.type_line.contains("Bear")); } }
Timing Interactions
Testing interactions with specific timing requirements:
#![allow(unused)] fn main() { #[test] fn test_timing_interactions() { let mut scenario = TestScenario::new(); let player1 = scenario.add_player(20); let player2 = scenario.add_player(20); // Set up cards let creature = scenario.add_card_to_battlefield("Grizzly Bears", player1); let counterspell = scenario.add_card_to_hand("Counterspell", player2); let bolt = scenario.add_card_to_hand("Lightning Bolt", player1); // Player 1 casts Lightning Bolt scenario.play_card(bolt, Some(player2)); // Player 2 responds with Counterspell scenario.pass_priority(player1); scenario.play_card(counterspell, Some(bolt)); // Resolve the stack scenario.resolve_stack(); // Verify Counterspell countered Lightning Bolt assert!(scenario.is_in_zone(bolt, Zone::Graveyard)); assert!(scenario.is_in_zone(counterspell, Zone::Graveyard)); assert_eq!(scenario.get_player_life(player2), 20); // Bolt was countered } }
Replacement Effect Interactions
Testing interactions between replacement effects:
#![allow(unused)] fn main() { #[test] fn test_replacement_effect_interactions() { let mut scenario = TestScenario::new(); let player = scenario.add_player(20); // Set up cards let creature = scenario.add_card_to_battlefield("Grizzly Bears", player); let prevention = scenario.add_card_to_battlefield("Circle of Protection: Red", player); let redirection = scenario.add_card_to_battlefield("Deflecting Palm", player); let bolt = scenario.add_card_to_hand("Lightning Bolt", player); // Activate Circle of Protection scenario.activate_ability(prevention, 0); scenario.pay_mana("{1}"); scenario.resolve_top_of_stack(); // Cast Lightning Bolt scenario.play_card(bolt, Some(player)); // Choose which replacement effect to apply first scenario.choose_replacement_effect(redirection); // Resolve the stack scenario.resolve_stack(); // Verify Deflecting Palm redirected the damage assert_eq!(scenario.get_player_life(player), 17); // Took 3 damage from redirection } }
Interaction Test Matrix
Complex interactions are organized in a test matrix:
- Row cards: First card in the interaction
- Column cards: Second card in the interaction
- Cell tests: Tests for the specific interaction
This ensures comprehensive coverage of card interactions.
Automated Interaction Discovery
The system can automatically discover potential interactions:
#![allow(unused)] fn main() { // Example of automated interaction discovery #[test] fn discover_interactions() { let interaction_finder = InteractionFinder::new(); // Find cards that interact with "Humility" let interactions = interaction_finder.find_interactions("Humility"); // Verify known interactions are discovered assert!(interactions.contains("Opalescence")); assert!(interactions.contains("Nature's Revolt")); assert!(interactions.contains("Dryad Arbor")); // Generate tests for each interaction for interaction in interactions { let test = interaction_finder.generate_test("Humility", &interaction); test.run(); } } }
Edge Case Testing
Interaction testing includes edge cases:
- Multiple simultaneous effects: Many effects applying at once
- Circular dependencies: Effects that depend on each other
- Priority edge cases: Complex priority passing scenarios
- Zone transition timing: Effects during zone transitions
Related Documentation
- Effect Verification: Testing individual card effects
- Complex Interactions: How complex interactions are implemented
- Layering System: Rules for effect layering