Spaces:
Sleeping
Sleeping
| use crate::core::logic::*; | |
| use crate::test_helpers::{create_test_db, create_test_state}; | |
| fn test_opcode_swap_cards_deck_refresh() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| ..Default::default() | |
| }; | |
| // Setup: Deck with 1 card, Discard with 3 cards. | |
| state.players[0].deck = vec![101].into(); | |
| state.players[0].discard = vec![201, 202, 203].into(); | |
| state.players[0].hand = vec![].into(); | |
| // Opcode 21: O_SWAP_CARDS, v=2 (move 2 cards), target_slot=6 (Hand) | |
| let bc = vec![O_SWAP_CARDS, 2, 0, 0, 6, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| println!("DECK: {:?}", state.players[0].deck); | |
| println!("HAND: {:?}", state.players[0].hand); | |
| assert_eq!( | |
| state.players[0].hand.len(), | |
| 2, | |
| "Hand size should be 2 after moving 2 cards from deck (including refresh)" | |
| ); | |
| assert_eq!( | |
| state.players[0].deck.len(), | |
| 2, | |
| "Deck size should be 2 after moving 2 cards from deck+discard (total 4)" | |
| ); | |
| } | |
| fn test_opcode_increase_cost_ripple() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| // Member 3000 baseline cost is 1 in create_test_db | |
| let base_cost = state.get_member_cost(0, 3000, -1, -1, &db, 0); | |
| // Opcode 70: O_INCREASE_COST, v=2 | |
| let bc = vec![O_INCREASE_COST, 2, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| let new_cost = state.get_member_cost(0, 3000, -1, -1, &db, 0); | |
| assert_eq!( | |
| new_cost, | |
| base_cost + 2, | |
| "Cost should increase by 2 (Baseline: {}, Actual: {})", | |
| base_cost, | |
| new_cost | |
| ); | |
| } | |
| fn test_opcode_select_live_rigor() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| state.players[0].live_zone[0] = 55001; | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| // Opcode 68: O_SELECT_LIVE, v=1 (Count 1) | |
| let bc = vec![O_SELECT_LIVE, 1, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!( | |
| state.phase, | |
| Phase::Response, | |
| "O_SELECT_LIVE should enter Phase::Response" | |
| ); | |
| assert_eq!( | |
| state | |
| .interaction_stack | |
| .last() | |
| .map(|i| i.choice_type) | |
| .unwrap_or(ChoiceType::None), | |
| ChoiceType::SelectLive | |
| ); | |
| // Submit choice for Live 55001 (idx 0) | |
| // SELECT_LIVE uses ACTION_BASE_STAGE_SLOTS for live zone slot selection | |
| let _ = state.step(&db, ACTION_BASE_STAGE_SLOTS + 0); | |
| assert_eq!(state.phase, Phase::Main, "Should resume to Phase::Main"); | |
| assert!( | |
| state.interaction_stack.is_empty(), | |
| "Interaction stack should be empty after response" | |
| ); | |
| } | |
| fn test_opcode_opponent_choose_rigor() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| state.debug.debug_mode = true; | |
| // Use card 3001 which has O_OPPONENT_CHOOSE -> O_DRAW bytecode | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| source_card_id: 3001, | |
| ..Default::default() | |
| }; | |
| // Get bytecode from card 3001 | |
| let bc = if let Some(m) = db.get_member(3001) { | |
| m.abilities[0].bytecode.clone() | |
| } else { | |
| panic!("Card 3001 not found in test DB"); | |
| }; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| assert_eq!(state.phase, Phase::Response); | |
| let interaction = state.interaction_stack.last().unwrap(); | |
| println!( | |
| "DEBUG: interaction.ctx.player_id = {}", | |
| interaction.ctx.player_id | |
| ); | |
| assert_eq!( | |
| interaction.ctx.player_id, 1, | |
| "Opponent (P1) should be the chooser" | |
| ); | |
| let _ = state.step(&db, ACTION_BASE_CHOICE + 0); // P1 chooses index 0 | |
| println!( | |
| "DEBUG: P0 hand len = {}, P1 hand len = {}", | |
| state.players[0].hand.len(), | |
| state.players[1].hand.len() | |
| ); | |
| assert_eq!(state.phase, Phase::Main); | |
| // Note: O_OPPONENT_CHOOSE flips ctx.player_id during resumption. | |
| // Subsequent opcodes (O_DRAW) use the flipped context. | |
| assert_eq!( | |
| state.players[1].hand.len(), | |
| 1, | |
| "Player 1 should have drawn the card due to context flip" | |
| ); | |
| } | |
| fn test_opcode_reduce_yell_count_functional() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| state.current_player = 0; | |
| state.players[0].deck = vec![3000, 3001, 3002].into(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| // Opcode 62: O_REDUCE_YELL_COUNT, v=1 | |
| let bc = vec![O_REDUCE_YELL_COUNT, 1, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| // Call do_yell for 2 cards | |
| let revealed = crate::core::logic::performance::do_yell(&mut state, &db, 2); | |
| assert_eq!( | |
| revealed.len(), | |
| 1, | |
| "Should reveal only 1 card due to reduction" | |
| ); | |
| } | |
| fn test_opcode_prevent_activate_rigor() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| state.current_player = 0; | |
| state.players[0].stage[0] = 3000; | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| // Opcode 82: O_PREVENT_ACTIVATE | |
| let bc = vec![O_PREVENT_ACTIVATE, 0, 0, 0, 0, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| // Verify prevent_activate flag is set | |
| assert_eq!( | |
| state.players[0].prevent_activate, 1, | |
| "prevent_activate flag should be set" | |
| ); | |
| // Cid 3121 has index 0 activated ability | |
| state.players[0].stage[0] = 3121; | |
| let res = state.activate_ability(&db, 0, 0); | |
| assert!(res.is_err(), "Activation should be blocked by restriction"); | |
| assert!(res.unwrap_err().contains("restriction")); | |
| } | |
| fn test_opcode_add_stage_energy_functional() { | |
| let db = create_test_db(); | |
| let mut state = create_test_state(); | |
| let ctx = AbilityContext { | |
| player_id: 0, | |
| area_idx: 0, | |
| ..Default::default() | |
| }; | |
| state.players[0].stage[0] = 3000; // Member in slot 0 (exists in test DB) | |
| state.players[0].stage_energy[0] = vec![].into(); | |
| state.players[0].deck = vec![2000, 2000].into(); | |
| // Add 2 stage energy. Opcode 50. | |
| let bc = vec![O_ADD_STAGE_ENERGY, 2, 0, 0, 4, O_RETURN, 0, 0, 0, 0]; | |
| state.resolve_bytecode_cref(&db, &bc, &ctx); | |
| // Verify stage_energy was added | |
| assert_eq!(state.players[0].stage_energy[0].len(), 2); | |
| assert_eq!(state.players[0].stage_energy_count[0], 2); | |
| // Verify the energy cards are CID 2000 (test energy card) | |
| assert_eq!(state.players[0].stage_energy[0][0], 2000); | |
| assert_eq!(state.players[0].stage_energy[0][1], 2000); | |
| } | |