"""Authored one-cell tabular Q updates; no environment or policy training loop."""
from fractions import Fraction as F
import json
def update(q,state,action,reward,next_state,terminated,truncated,alpha,gamma):
    if type(terminated) is not bool or type(truncated) is not bool:raise ValueError('two explicit bool flags')
    if not 0<alpha<=1 or not 0<=gamma<=1:raise ValueError('alpha/gamma domain')
    old=q[state][action]
    future=F(0) if terminated else max(q[next_state].values())
    target=reward+gamma*future;new=old+alpha*(target-old)
    q[state][action]=new
    return {'old':str(old),'future':str(future),'reward':str(reward),'target':str(target),'td_error':str(target-old),'new':str(new),'terminated':terminated,'truncated':truncated}
def demo():
    initial={'A':{'L':F(2),'R':F(0)},'B':{'L':F(1),'R':F(4)}};rows=[]
    for terminated,truncated in [(False,False),(True,False),(False,True)]:
        q={s:dict(v) for s,v in initial.items()};r=update(q,'A','L',F(1),'B',terminated,truncated,F(1,4),F(9,10));rows.append(r)
        assert q['A']['R']==0 and q['B']==initial['B']
    assert [r['new'] for r in rows]==['53/20','7/4','53/20']
    assert rows[0]['target']=='23/5' and rows[1]['target']=='1'
    # A true terminal state does not need a successor entry.
    q={'A':{'L':F(2)}};assert update(q,'A','L',F(1),'not_in_table',True,False,F(1,4),F(9,10))['new']=='7/4'
    return {'initial_table':{s:{a:str(x) for a,x in v.items()} for s,v in initial.items()},'alpha':'1/4','gamma':'9/10','separate_updates_from_same_initial_table':rows,'terminal_successor_lookup_skipped':True,'environment_executed':False,'learned_policy_or_convergence_claimed':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
