File size: 5,909 Bytes
0dd780c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import json
import os
import sys

# Add project root to path
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

import engine_rust

from ai.utils.benchmark_decks import parse_deck
from engine.game.data_loader import CardDataLoader  # Only for descriptions
from engine.game.desc_utils import get_action_desc
from engine.game.game_state import GameState  # Only for descriptions


def verify_abilities():
    print("--- AI Ability Visibility Verification (Rust Engine) ---")

    # 1. Load DB
    db_path = "engine/data/cards_compiled.json"
    with open(db_path, "r", encoding="utf-8") as f:
        db_content = f.read()
    db_json = json.loads(db_content)

    # Initialize Python GameState for descriptions
    loader = CardDataLoader(db_path)
    members, lives, energy = loader.load()
    GameState.initialize_class_db(members, lives)
    py_gs = GameState()

    # 2. Setup Rust Engine State
    db = engine_rust.PyCardDatabase(db_content)
    gs = engine_rust.PyGameState(db)

    # Load a benchmark deck to get valid IDs
    deck_file = "ai/decks/liella_cup.txt"
    main_deck, lives_deck, energy_deck = parse_deck(
        deck_file, db_json["member_db"], db_json["live_db"], db_json.get("energy_db", {})
    )

    # Standard sizes
    test_lives = (lives_deck * 10)[:12]
    test_deck = (main_deck * 10)[:48]
    test_energy = (energy_deck * 10)[:12]

    gs.initialize_game(test_deck, test_deck, test_energy, test_energy, test_lives, test_lives)

    # Fast-forward to Main Phase
    # Phase Flow: MULLIGAN_P1 -> MULLIGAN_P2 -> LIVE_SET -> MAIN
    print(f"Skipping Mulligan P1 (Current: {gs.phase})...")
    gs.step(0)  # Confirm Mulligan P1
    print(f"Skipping Mulligan P2 (Current: {gs.phase})...")
    gs.step(0)  # Confirm Mulligan P2

    # In Live Set, we need to set a card.
    # Actions 400-459 are Live Set.
    # Player 1 (index 0) sets a card.
    print(f"Setting Live Card (Current: {gs.phase})...")
    gs.step(400)  # Set first card in hand as live
    # Player 2 (index 1) sets a card.
    gs.step(400)

    # Sync Python GS for descriptions
    hand = list(gs.get_player(0).hand)
    py_gs.players[0].hand = hand
    py_gs.current_player = gs.current_player
    py_gs.phase = gs.phase

    # 3. Find or Inject Ability Card
    ability_card_id = -1
    hand_idx = -1
    for i, cid in enumerate(hand):
        if cid in members:
            card = members[cid]
            # Check for ACTIVATED trigger
            if any(ab.trigger.name == "ACTIVATED" for ab in card.abilities):
                ability_card_id = cid
                hand_idx = i
                break

    # Sync Python GS for descriptions
    hand = list(gs.get_player(0).hand)
    py_gs.players[0].hand = hand
    py_gs.current_player = gs.current_player
    py_gs.phase = gs.phase

    # 3. Inject and Play Ability Card (Keke ID 247)
    target_cid = 247
    print(f"Injecting Card ID {target_cid} into hand...")
    # Update Rust State
    gs.get_player(0).hand = [target_cid] + list(gs.get_player(0).hand)
    # Update Python Stage for get_action_desc
    py_gs.players[0].hand = [target_cid] + list(py_gs.players[0].hand)

    # Check if card has an activated ability in py_gs
    card = members[target_cid]
    abs = [ab for ab in card.abilities if ab.trigger.name == "ACTIVATED"]
    print(f"Card {card.name} has {len(abs)} activated abilities.")
    for ab in abs:
        print(f" - Ability: {ab.raw_text}")

    print(f"Playing {get_action_desc(500, py_gs)} to Center...")
    # Action ID 2 (Hand 0 to Center)
    gs.step(2)

    # Update Python Stage
    py_gs.players[0].stage[1] = target_cid
    py_gs.players[0].hand = list(gs.get_player(0).hand)

    # Check legality
    def check_legal(gs, py_gs):
        legal_actions = list(gs.get_legal_action_ids())
        print(f"Phase: {gs.phase} | Legal IDs: {len(legal_actions)}")
        if 201 in legal_actions:
            print("SUCCESS: Action 201 (Center Ability) is legal.")
            return True
        print("Action 201 is NOT legal.")
        if len(legal_actions) < 20:  # Show only if list is small
            for a in legal_actions:
                print(f"  - Action {a}: {get_action_desc(a, py_gs)}")
        return False

    if not check_legal(gs, py_gs):
        print("Skipping to next turn's Main Phase...")
        # Finish Turn 1
        gs.step(0)  # End Main
        gs.step(0)  # End Live Start
        gs.step(0)  # End Live Set
        gs.step(0)  # End Performance
        gs.step(0)  # End Turn

        # Turn 2
        gs.step(0)  # End Active/Standby
        gs.step(0)  # End Draw

        # Now we should be in Turn 2 Main Phase
        # Re-sync Python GS
        py_gs.phase = gs.phase
        py_gs.current_player = gs.current_player

        check_legal(gs, py_gs)

    # 4. Run MCTS
    model_path = "ai/models/alphanet_best.onnx"
    mcts = engine_rust.PyHybridMCTS(model_path, 0.0)

    print("\nRunning MCTS (1600 sims)...")
    suggestions = mcts.get_suggestions(gs, 1600)

    print("\nTop MCTS Suggestions:")
    for action_id, score, visits in sorted(suggestions, key=lambda x: x[2], reverse=True)[:10]:
        desc = get_action_desc(int(action_id), py_gs)
        print(f"  Action {action_id:4}: {visits:5} visits | {score:.4f} score | {desc}")

    # 5. Final confirmation
    ability_actions = [a for (a, s, v) in suggestions if 200 <= a <= 202]
    if ability_actions:
        print(f"\nCONFIRMED: MCTS explored ability actions: {[int(a) for a in ability_actions]}")
        for a in ability_actions:
            print(f" - {get_action_desc(int(a), py_gs)}")
    else:
        print("\nISSUE: MCTS did NOT explore ability actions.")


if __name__ == "__main__":
    verify_abilities()