"""Exact three-player ICM spectator examples; Python 3 standard library only.
Run in any folder: python generate.py. Outputs are written beside this script.
No empirical hands, random draws, strategy solution or currency rounding model.
"""
from fractions import Fraction as F
from pathlib import Path
import csv, json

ROOT=Path(__file__).resolve().parent
def equities(stacks,prizes):
    # Closed three-player formula. At a single elimination, the zero stack
    # receives third prize and survivors compete for first and second.
    total=sum(stacks)
    assert len(stacks)==len(prizes)==3 and total>0 and min(stacks)>=0
    assert sum(s==0 for s in stacks)<=1
    result=[]
    for i,s in enumerate(stacks):
        if not s:
            result.append(F(prizes[2])); continue
        first=F(s,total)
        second=sum((F(t,total)*F(s,total-t) for j,t in enumerate(stacks) if j!=i and t),F(0))
        third=1-first-second
        result.append(first*prizes[0]+second*prizes[1]+third*prizes[2])
    assert sum(result)==sum(prizes)
    return result
def encoded(values):
    return [{'exact':str(x),'dollars':float(x)} for x in values]

prizes=[500,300,200]
states={'before':[20,60,20],'large_wins':[20,80,0],'short_wins':[20,40,40]}
values={k:equities(v,prizes) for k,v in states.items()}
expected=[(a+b)/2 for a,b in zip(values['large_wins'],values['short_wins'])]
baseline=values['before'][0]
threshold=(baseline-values['short_wins'][0])/(values['large_wins'][0]-values['short_wins'][0])
result={'question':'Can an unchanged spectator stack lose ICM equity in one outcome?',
 'players':['Hero','A','B'],'stack_unit':'1000 chips','prizes_dollars':prizes,
 'states':{k:{'stacks':s,'equities':encoded(values[k])} for k,s in states.items()},
 'fair_large_win_probability':'1/2','fair_expected_equities':encoded(expected),
 'fair_expected_changes':encoded([a-b for a,b in zip(expected,values['before'])]),
 'break_even_large_win_probability':str(threshold),
 'biased_large_win_probability':'1/10',
 'biased_hero_equity':encoded([values['large_wins'][0]/10+values['short_wins'][0]*F(9,10)])[0]}
(ROOT/'results.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8',newline='\n')
with (ROOT/'probability-sensitivity.csv').open('w',encoding='utf-8',newline='') as f:
    w=csv.writer(f,lineterminator='\n'); w.writerow(['large_win_probability','hero_equity_exact_dollars','hero_equity_dollars','change_exact_dollars'])
    for numerator in range(101):
        p=F(numerator,100); ev=p*values['large_wins'][0]+(1-p)*values['short_wins'][0]
        w.writerow([str(p),str(ev),format(float(ev),'.8f'),str(ev-baseline)])
with (ROOT/'transfer-sensitivity.csv').open('w',encoding='utf-8',newline='') as f:
    w=csv.writer(f,lineterminator='\n');w.writerow(['profile','transfer_1000_chips','hero_before_exact_dollars','hero_fair_mean_exact_dollars','hero_change_exact_dollars'])
    for name,payout in [('tiered',[500,300,200]),('winner_take_all',[1000,0,0]),('equal',[300,300,300])]:
        before=equities([20,60,20],payout)[0]
        for amount in range(21):
            avg=(equities([20,60+amount,20-amount],payout)[0]+equities([20,60-amount,20+amount],payout)[0])/2
            w.writerow([name,amount,str(before),str(avg),str(avg-before)])
print('Generated 3 states, 101 probability rows, 63 transfer/payout rows.')
