# https://www.youtube.com/watch?v=8xXslshomOs
from typing import Dict, Optional
from dataclasses import dataclass


@dataclass(frozen=True, order=True)
class State:
    gold_a: int
    gold_b: int
    silver_a: int
    silver_b: int
    gems_a: int
    gems_b: int
    equal_priority: bool  # 0 means "a", which is the protagonist


winner_cache : Dict[State, int] = {}

def resolve_auction(state: State, bet_a: int, bet_b: int) -> Optional[State]:
    if bet_b > state.gems_b:
        return None
    assert bet_a <= state.gems_a

    equal_priority = state.equal_priority
    if bet_a > bet_b:
        bet_winner = 0
    elif bet_a < bet_b:
        bet_winner = 1
    else:
        bet_winner = int(equal_priority)
        equal_priority = 1 - equal_priority

    doing_golds = state.gold_a + state.gold_b <3
    return State(
        gold_a=state.gold_a + 1 if doing_golds and bet_winner == 0 else state.gold_a,
        gold_b=state.gold_b + 1 if doing_golds and bet_winner == 1 else state.gold_b,
        silver_a=state.silver_a + 1 if not doing_golds and bet_winner == 0 else state.silver_a,
        silver_b=state.silver_b + 1 if not doing_golds and bet_winner == 1 else state.silver_b,
        gems_a=state.gems_a - bet_a if bet_winner == 0 else state.gems_a,
        gems_b=state.gems_b - bet_b if bet_winner == 1 else state.gems_b,
        equal_priority=equal_priority,
    )


next_print = 1

def get_winner(state: State) -> int:
    if state.gold_a >= 3:
        winner_cache[state] = 0; return 0, (state,)
    if state.gold_b >= 3:
        winner_cache[state] = 1; return 1, (state,)
    if state.silver_a >= 3:
        winner_cache[state] = 0; return 0, (state,)
    if state.silver_b >= 3:
        winner_cache[state] = 1; return 1, (state,)
    winner = winner_cache.get(state)
    if winner is not None:
        return winner, (state,)


    doing_golds = state.gold_a + state.gold_b <3
    child_states = []
    path = tuple()

    # minimax
    winner_a = 1
    for bet_a in range(state.gems_a+1):
        (winner_b, path) = max([
            get_winner(child)
            for child in
                [resolve_auction(state, bet_a, bet_b) for bet_b in {0, bet_a, bet_a+1}]
            if child
        ])
        winner_a = min(winner_a, winner_b)
        if winner_a == 0:
            path = (state, bet_a) + path
            # break

    winner_cache[state] = winner_a
    global next_print
    if len(winner_cache) > next_print:
        print(len(winner_cache))
        next_print *= 2

    return winner_a, path

initial_state = State(
    gold_a=1,
    gold_b=0,
    silver_a=0,
    silver_b=0,
    gems_a=75,
    gems_b=99,
    equal_priority=0,
)
import pickle
cache_file = __file__+".winner_cache.pickle"

# print(get_winner(initial_state))
# with open(cache_file, 'wb') as f:
#     pickle.dump(winner_cache, f)

with open(cache_file, 'rb') as f:
    winner_cache = pickle.load(f)

# print(winner_cache)

def find_children(state):
    child_states = []
    for bet_a in range(state.gems_a+1):
        responses = [resolve_auction(state, bet_a, bet_b) for bet_b in {0, bet_a, bet_a+1}]
        if any(winner_cache.get(res) == 1 for res in responses):
            responses = [res for res in responses if winner_cache.get(res) == 1]
        child_states.extend(responses)
    return [state for state in child_states if state is not None]

def find_winning_children(state):
    return [state for state in find_children(state) if winner_cache.get(state) == 0]

def print_bets(state0, state1):
    for bet_a in range(state0.gems_a+1):
        for bet_b in {0, bet_a, bet_a+1}:
            res = resolve_auction(state0, bet_a, bet_b)
            if res == state1:
                print(f'bet_a={bet_a} bet_b={bet_b}')


def get_winning_path(state: State):
    get_winning_option_fraction = lambda child: len(find_winning_children(child)) / max(1, len(find_children(child)))
    print()
    print(len(find_winning_children(state)), "/", len(find_children(state)))
    print(state)


    winner = winner_cache.get(state)

    child_states = find_winning_children(state)
    if not child_states:
        print('fin')
        return

    choice = min(child_states, key=get_winning_option_fraction)
    print_bets(state, choice)
    # for child in child_states:
    #     print(" " + (">" if child is choice else " ") + " " + repr(child))
    get_winning_path(choice)

get_winning_path(initial_state)
