1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See Prediction.mli *)type'modelt={me:int;update:stringarray->'model->'model;mutablemodel:'model;(* the others' inputs, guessed: their latest *)mutablelatest:stringarray;(* mine not yet confirmed: their number, the input, and the model I
* predicted after it (to compare with the server's) *)mutablepending:(int*string*'model)list;mutablemispredictions:int;}letcreate~(me:int)~(players:int)~update(model:'model):'modelt={me;update;model;latest=Array.makeplayers"";pending=[];mispredictions=0}(* one tick: my input, the others' latest *)letplay(t:'modelt)(input:string)(model:'model):'model=t.update(Array.mapi(funplatest->ifp=t.metheninputelselatest)t.latest)modelletstep(t:'modelt)~(seq:int)(input:string):unit=t.model<-playtinputt.model;t.pending<-t.pending@[(seq,input,t.model)]letcorrect(t:'modelt)~(world:'model)~(acked:int)~(latest:stringarray):unit=(* what I had predicted after [acked], against the server's *)(matchList.find_opt(fun(seq,_,_)->seq=acked)t.pendingwith|Some(_,_,predicted)whenChecksum.of_modelpredicted<>Checksum.of_modelworld->t.mispredictions<-t.mispredictions+1|_->());t.latest<-latest;t.pending<-List.filter(fun(seq,_,_)->seq>acked)t.pending;(* the server's world, mine not yet applied played again on it *)letmodel,pending=List.fold_left(fun(model,acc)(seq,input,_)->letmodel=playtinputmodelin(model,(seq,input,model)::acc))(world,[])t.pendingint.model<-model;t.pending<-List.revpendingletmodel(t:'modelt):'model=t.modelletmispredictions(t:'modelt):int=t.mispredictions