use crate::core::logic::*; use crate::core::enums::*; use crate::core::models::*; use std::collections::HashMap; fn create_test_db() -> CardDatabase { let mut db = CardDatabase { members: HashMap::new(), lives: HashMap::new(), members_vec: vec![None; 1000], lives_vec: vec![None; 40000], }; // Member with cost 5 db.members.insert(10, MemberCard { card_id: 10, card_no: "M-10".to_string(), name: "Member 10".to_string(), cost: 5, ..Default::default() }); db.members_vec[10] = Some(db.members[&10].clone()); // Member with cost 15 db.members.insert(15, MemberCard { card_id: 15, card_no: "M-15".to_string(), name: "Member 15".to_string(), cost: 15, ..Default::default() }); db.members_vec[15] = Some(db.members[&15].clone()); // Live card db.lives.insert(10050, LiveCard { card_id: 10050, card_no: "L-10050".to_string(), name: "Live 10050".to_string(), ..Default::default() }); db.lives_vec[50] = Some(db.lives[&10050].clone()); db } fn create_test_state() -> GameState { let mut state = GameState::default(); state.players[0].player_id = 0; state.players[1].player_id = 1; state.phase = Phase::Main; state } #[test] fn test_opcode_draw_until() { let db = create_test_db(); let mut state = create_test_state(); state.players[0].deck = vec![1, 2, 3, 4, 5]; state.players[0].hand = vec![101, 102]; // Hand size 2 let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_DRAW_UNTIL 5 (Draw up to 5) let bc = vec![O_DRAW_UNTIL, 5, 0, 0, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); assert_eq!(state.players[0].hand.len(), 5); assert_eq!(state.players[0].deck.len(), 2); } #[test] fn test_opcode_reveal_until_type_live() { let db = create_test_db(); let mut state = create_test_state(); // Deck: 10 (member), 15 (member), 10050 (live), 1 (fallback) state.players[0].deck = vec![1, 10050, 15, 10]; let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_REVEAL_UNTIL 33 (TYPE_CHECK) attr: 1 (Live), target: 6 (Hand) let bc = vec![O_REVEAL_UNTIL, 33, 1, 6, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); // Should have popped 10, 15, then 10050. // 10050 matches Live. It goes to hand. // 10 and 15 go to discard. assert!(state.players[0].hand.contains(&10050)); assert_eq!(state.players[0].discard.len(), 2); // 10 and 15 assert!(state.players[0].discard.contains(&10)); assert!(state.players[0].discard.contains(&15)); assert_eq!(state.players[0].deck.len(), 1); // 1 remains } #[test] fn test_opcode_reveal_until_cost_ge() { let db = create_test_db(); let mut state = create_test_state(); // Deck: 10 (cost 5), 15 (cost 15), 1 (fallback) state.players[0].deck = vec![1, 15, 10]; let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_REVEAL_UNTIL 16 (COST_GE) attr: (10 << 1) | 0x40 = 20 | 64 = 84, target: 6 (Hand) // Wait, attr in logic.rs: min_cost = (a >> 1) & 0x1F let bc = vec![O_REVEAL_UNTIL, 16, 84, 6, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); // Should pop 10 (cost 5 < 10), then 15 (cost 15 >= 10). // 15 goes to hand. 10 goes to discard. assert!(state.players[0].hand.contains(&15)); assert_eq!(state.players[0].discard.len(), 1); assert!(state.players[0].discard.contains(&10)); } #[test] fn test_opcode_immunity() { let db = create_test_db(); let mut state = create_test_state(); assert!(!state.players[0].has_immunity); let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_IMMUNITY 1 let bc = vec![O_IMMUNITY, 1, 0, 0, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); assert!(state.players[0].has_immunity); // O_IMMUNITY 0 let bc = vec![O_IMMUNITY, 0, 0, 0, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); assert!(!state.players[0].has_immunity); } #[test] fn test_opcode_pay_energy() { let db = create_test_db(); let mut state = create_test_state(); state.players[0].tapped_energy = vec![false, false, false]; let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_PAY_ENERGY 2 let bc = vec![O_PAY_ENERGY, 2, 0, 0, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); assert_eq!(state.players[0].tapped_energy.iter().filter(|&&t| t).count(), 2); } #[test] fn test_opcode_look_deck() { let db = create_test_db(); let mut state = create_test_state(); state.players[0].deck = vec![1, 2, 3, 4, 5]; let ctx = AbilityContext { player_id: 0, ..Default::default() }; // O_LOOK_DECK 3 let bc = vec![O_LOOK_DECK, 3, 0, 0, O_RETURN, 0, 0, 0]; state.resolve_bytecode(&db, &bc, &ctx); assert_eq!(state.players[0].looked_cards.len(), 3); assert_eq!(state.players[0].deck.len(), 2); assert_eq!(state.players[0].looked_cards, vec![5, 4, 3]); }