123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657(* 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 Bot.mli *)type('world,'senses,'intent)t={sense:'sensesoption->'world->'senses;decide:'senses->'intent;delay:int;rate:int;reflex:'world->'intent->'intent;}letmake?(delay=0)?(rate=1)?(reflex=fun_intent->intent)~(sense:'sensesoption->'world->'senses)~(decide:'senses->'intent)():('world,'senses,'intent)t={sense;decide;delay;rate=max1rate;reflex}(* [memory] holds the last [delay] + 1 senses, the newest first *)type('senses,'intent)running={memory:'senseslist;last:'intent;frame:int}letstart(intent:'intent):('senses,'intent)running={memory=[];last=intent;frame=0}letrecnth_or_last(l:'alist)(n:int):'aoption=matchlwith[]->None|[x]->Somex|x::rest->ifn<=0thenSomexelsenth_or_lastrest(n-1)letstep(bot:('world,'senses,'intent)t)(world:'world)(r:('senses,'intent)running):'intent*('senses,'intent)running=letmemory=bot.sense(matchr.memorywiths::_->Somes|[]->None)world::r.memoryin(* keep what the delay needs, no more *)letmemory=List.filteri(funi_->i<=bot.delay)memoryinletintent=ifr.framemodbot.rate<>0thenr.lastelsematchnth_or_lastmemorybot.delaywithSomes->bot.decides|None->r.lastin(* claude: the decision is what is kept and repeated; the reflex
* adjusts it to the world of this frame, every frame *)(bot.reflexworldintent,{memory;last=intent;frame=r.frame+1})letlast_senses(r:('senses,'intent)running):'sensesoption=matchr.memorywiths::_->Somes|[]->None(* a smooth wobble in [-1, 1]: two sines that don't share a period, so
* it doesn't repeat visibly *)letwobble(t:float)(seed:int):float=lets=float_of_intseedin(sin((t*.0.37)+.(s*.1.7))+.sin((t*.0.11)+.(s*.0.9)))/.2.letaim_error~(spread:float)~(settle:float)~(seen_for:int)~(seed:int)():float=letdecay=ifsettle<=0.then0.else0.5**(float_of_intseen_for/.settle)inspread*.decay*.wobble(float_of_intseen_for)seed