use crate::core::logic::*; use serde_json::json; #[test] fn test_rule_8_3_16_all_or_nothing_failure() { let mut db = CardDatabase::default(); // Card 0: Impossible (needs 100 Red hearts) let mut impossible_live = LiveCard::default(); impossible_live.card_id = 0; impossible_live.hearts_board.set_color_count(1, 100); db.lives.insert(0, impossible_live.clone()); db.lives_vec[0 as usize] = Some(impossible_live); // Card 1: Easy (needs 0 hearts) let mut easy_live = LiveCard::default(); easy_live.card_id = 1; db.lives.insert(1, easy_live.clone()); db.lives_vec[1 as usize] = Some(easy_live); let mut state = GameState::default(); state.phase = Phase::LiveResult; // P0 sets both: 0 (impossible) and 1 (easy) state.players[0].live_zone[0] = 0; state.players[0].live_zone[1] = 1; // Run judgement state.do_live_result(&db); // Rule 8.3.16: Since one failed, both should be discarded assert_eq!(state.players[0].live_zone[0], -1); assert_eq!(state.players[0].live_zone[1], -1); assert_eq!(state.players[0].success_lives.len(), 0); assert!(state.players[0].discard.contains(&0)); assert!(state.players[0].discard.contains(&1)); } #[test] fn test_rule_8_4_13_first_player_change() { let mut state = GameState::default(); state.first_player = 0; state.current_player = 0; state.phase = Phase::LiveResult; // Only P1 gets a success live state.obtained_success_live = [false, true]; // Finalize state.finalize_live_result(); // Rule 8.4.13: P1 becomes first player assert_eq!(state.first_player, 1); assert_eq!(state.current_player, 1); } #[test] fn test_rule_8_4_13_first_player_no_change_both_success() { let mut state = GameState::default(); state.first_player = 0; state.current_player = 0; state.phase = Phase::LiveResult; // Both get success lives state.obtained_success_live = [true, true]; // Finalize state.finalize_live_result(); // Rule 8.4.13: Turn order unchanged assert_eq!(state.first_player, 0); } #[test] fn test_priority_p1_triggers_first() { let mut db = CardDatabase::default(); db.members_vec.resize(1000, None); // Member with OnLiveSuccess: +100 Score let mut m1 = MemberCard::default(); m1.card_id = 101; m1.name = "P0 Member".to_string(); m1.abilities.push(Ability { trigger: TriggerType::OnLiveSuccess, bytecode: vec![O_BOOST_SCORE, 100, 0, 0, 0, O_RETURN, 0, 0, 0, 0], ..Default::default() }); db.members.insert(101, m1.clone()); db.members_vec[101 as usize] = Some(m1); // Member with OnLiveSuccess: +200 Score let mut m2 = MemberCard::default(); m2.card_id = 102; m2.name = "P1 Member".to_string(); m2.abilities.push(Ability { trigger: TriggerType::OnLiveSuccess, bytecode: vec![O_BOOST_SCORE, 200, 0, 0, 0, O_RETURN, 0, 0, 0, 0], ..Default::default() }); db.members.insert(102, m2.clone()); db.members_vec[102 as usize] = Some(m2); let mut state = GameState::default(); state.first_player = 1; // P1 IS FIRST PLAYER state.phase = Phase::LiveResult; // P0 has m1 on stage state.players[0].stage[0] = 101; // P1 has m2 on stage state.players[1].stage[0] = 102; // Both had successful lives (snapshot) state .ui .performance_results .insert(0, json!({"success": true})); state .ui .performance_results .insert(1, json!({"success": true})); // Run judgement state.do_live_result(&db); // Verify order in rule_log // Order should be P1 trigger (Score +200) then P0 trigger (Score +100) let logs: Vec = state .ui .rule_log .as_ref() .unwrap() .iter() .filter(|s| s.contains("Score +")) .cloned() .collect(); assert_eq!(logs.len(), 2); assert!( logs[0].contains("Score +200"), "P1 (First Player) should trigger first. Logs: {:?}", logs ); assert!( logs[1].contains("Score +100"), "P0 should trigger second. Logs: {:?}", logs ); } #[test] fn test_priority_p1_choice_selection() { let mut db = CardDatabase::default(); db.lives_vec.resize(1000, None); // Setup lives let mut l1 = LiveCard::default(); l1.card_id = 1; l1.name = "L1".to_string(); db.lives.insert(1, l1.clone()); db.lives_vec[1] = Some(l1); let mut l2 = LiveCard::default(); l2.card_id = 2; l2.name = "L2".to_string(); db.lives.insert(2, l2.clone()); db.lives_vec[2] = Some(l2); let mut state = GameState::default(); state.first_player = 1; // P1 IS FIRST PLAYER state.phase = Phase::LiveResult; // Both players have 2 candidates (multiple candidates = choice needed) state.players[0].live_zone[0] = 1; state.players[0].live_zone[1] = 2; state.players[1].live_zone[0] = 1; state.players[1].live_zone[1] = 2; // Snapshot: both succeeded state.ui.performance_results.insert( 0, json!({ "success": true, "lives": [{"passed": true, "score": 10}, {"passed": true, "score": 10}] }), ); state.ui.performance_results.insert( 1, json!({ "success": true, "lives": [{"passed": true, "score": 10}, {"passed": true, "score": 10}] }), ); // Skip OnLiveSuccess triggers to reach choice logic state.live_result_processed_mask = [0x80, 0x80]; state.do_live_result(&db); // P1 (First Player) should be current_player for choice assert_eq!( state.current_player, 1, "P1 should be selected for choice first when P1 is first_player" ); assert!(state.live_result_selection_pending); }