python/Dongle's Difficult Dilemma.py
DomNomNomVR 028fb0bc3a dump
2025-04-14 15:58:38 +12:00

149 lines
4.3 KiB
Python

# 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)