rabukasim / engine_rust_src /src /ability_tests.rs
trioskosmos's picture
Upload folder using huggingface_hub
463f868 verified
use crate::core::logic::*;
use crate::test_helpers::{create_test_state, load_real_db};
/// Verifies that O_DRAW and O_MOVE_TO_DISCARD correctly manipulate hand and deck using real card IDs.
#[test]
fn test_opcode_draw_discard() {
let db = load_real_db(); // Use production DB
let mut state = create_test_state();
state.ui.silent = true;
// Use real card IDs: 121 (Eli), 124 (Rin)
state.players[0].deck = vec![121, 124, 121, 124, 121].into();
let ctx = AbilityContext {
player_id: 0,
..Default::default()
};
// O_DRAW 2
let bc = vec![O_DRAW, 2, 0, 0, 0, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].hand.len(), 2);
assert_eq!(state.players[0].deck.len(), 3);
assert!(state.players[0].hand.contains(&121) || state.players[0].hand.contains(&124));
// O_MOVE_TO_DISCARD 1 (attr 2 = Hand)
// Pre-seed choice_index so it doesn't suspend, since inline bytecode can't be resumed
let discard_ctx = AbilityContext {
player_id: 0,
choice_index: 0,
..Default::default()
};
let bc = vec![O_MOVE_TO_DISCARD, 1, 2, 0, 6, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &discard_ctx);
assert_eq!(state.players[0].hand.len(), 1);
assert_eq!(state.players[0].discard.len(), 1);
}
/// Verifies that O_ADD_BLADES, O_ADD_HEARTS, and O_BOOST_SCORE correctly apply stat buffs.
#[test]
fn test_opcode_stats_boost() {
let db = load_real_db();
let mut state = create_test_state();
state.players[0].stage[0] = 121; // Real card ID
let ctx = AbilityContext {
player_id: 0,
area_idx: 0,
..Default::default()
};
// O_ADD_BLADES 2 to SELF (Slot 4)
let bc = vec![O_ADD_BLADES, 2, 0, 0, 4, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].blade_buffs[0], 2);
// O_ADD_HEARTS 3 (Pink=0) to SELF (Slot 4)
let bc = vec![O_ADD_HEARTS, 3, 0, 0, 4, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].heart_buffs[0].get_color_count(0), 3);
// O_BOOST_SCORE 5 to SELF
let bc = vec![O_BOOST_SCORE, 5, 0, 0, 0, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].live_score_bonus, 5);
}
/// Verifies that O_SET_TAPPED can both tap and untap members.
#[test]
fn test_opcode_tap_untap() {
let db = load_real_db();
let mut state = create_test_state();
state.players[0].stage[1] = 124; // Real card ID
state.players[0].set_tapped(1, false);
let ctx = AbilityContext {
player_id: 0,
area_idx: 1,
..Default::default()
};
// O_SET_TAPPED 1 SELF
let bc = vec![O_SET_TAPPED, 1, 0, 0, 4, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert!(state.players[0].is_tapped(1));
// O_SET_TAPPED 0 SELF
let bc = vec![O_SET_TAPPED, 0, 0, 0, 4, O_RETURN, 0, 0, 0, 0];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert!(!state.players[0].is_tapped(1));
}
/// Verifies that conditional jumps (O_JUMP_F) work correctly based on card count in hand (C_COUNT_HAND).
#[test]
fn test_conditions_basic() {
let db = load_real_db();
let mut state = create_test_state();
state.players[0].hand = vec![121, 124, 121].into();
let ctx = AbilityContext {
player_id: 0,
..Default::default()
};
let bc = vec![
C_COUNT_HAND,
3,
0,
0,
0,
O_JUMP_IF_FALSE,
1,
0,
0,
0,
O_DRAW,
1,
0,
0,
0,
O_RETURN,
0,
0,
0,
0,
];
state.players[0].deck = vec![124].into();
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].hand.len(), 4);
// C_COUNT_HAND GE 5 (False) -> Draw 1
let mut state = create_test_state();
state.players[0].hand = vec![121, 124, 121].into();
state.players[0].deck = vec![124].into();
let bc = vec![
C_COUNT_HAND,
5,
0,
0,
0,
O_JUMP_IF_FALSE,
1,
0,
0,
0,
O_DRAW,
1,
0,
0,
0,
O_RETURN,
0,
0,
0,
0,
];
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.players[0].hand.len(), 3);
}
/// Verifies that O_LOOK_AND_CHOOSE correctly defaults to deck and moves remainder to discard using real data.
#[test]
fn test_look_and_choose_remainder() {
let db = load_real_db();
let mut state = create_test_state();
state.players[0].deck = vec![121, 124, 121, 124, 121].into();
let ctx = AbilityContext {
player_id: 0,
..Default::default()
};
// O_LOOK_DECK 4 -> O_LOOK_AND_CHOOSE 1 to Hand (Source 6)
let bc = vec![
O_LOOK_DECK,
4,
0,
0,
0,
O_LOOK_AND_CHOOSE,
1,
0,
0,
6,
O_RETURN,
0,
0,
0,
0,
];
// Execution 1: Reveal cards
state.resolve_bytecode_cref(&db, &bc, &ctx);
assert_eq!(state.phase, Phase::Response);
assert_eq!(state.players[0].looked_cards.len(), 4);
assert_eq!(state.players[0].deck.len(), 1);
// Simulated selection of index 1
let mut state2 = state.clone();
let mut ctx2 = state2
.interaction_stack
.last()
.expect("Missing pending_interaction")
.ctx
.clone();
ctx2.choice_index = 1;
state2.resolve_bytecode_cref(&db, &bc, &ctx2);
assert_eq!(state2.players[0].hand.len(), 1);
assert_eq!(state2.players[0].deck.len(), 4); // 1 unlooked + 3 remainder
assert_eq!(state2.players[0].looked_cards.len(), 0);
// Execution 2: Skip selection (999)
let mut state3 = state.clone();
let mut ctx3 = state3
.interaction_stack
.last()
.expect("Missing pending_interaction")
.ctx
.clone();
ctx3.choice_index = 999;
state3.resolve_bytecode_cref(&db, &bc, &ctx3);
assert_eq!(state3.players[0].hand.len(), 0);
assert_eq!(state3.players[0].deck.len(), 5); // All 4 + 1 back to deck
}