Spaces:
Sleeping
Sleeping
File size: 6,072 Bytes
463f868 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | 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<String> = 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);
}
|