"""Execute reserve decisions and commits from production, with detached HQ reports.""" import re from policy_harness import ROOT, body, run p = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterService.c' def translate(s): s = s.replace('protected ', '').replace('foreach (', 'for (').replace('null', 'nullptr') for typ in ('ERR_HQWatchEntry', 'ERR_HunterVehicle', 'ERR_HQ'): s = re.sub(r'\b' + typ + r' (?=\w)', typ + '* ', s) for name in ('entry', 'unit', 'hq', 'm_PendingReserve'): s = s.replace(name + '.', name + '->') return (s.replace('Watch().', 'Watch()->').replace('int count;', 'int count=0;') .replace('ERR_HunterFleetRules.', 'ERR_HunterFleetRules::') .replace('ERR_HQ.Event', 'ERR_HQ::Event').replace('string.Format', 'Format')) rules = '\n'.join(translate(body(p, sig)) for sig in ('static bool Unavailable(', 'static bool ReserveDue(')) methods = '\n'.join(translate(body(p, sig)) for sig in ( 'int Deployed(', 'protected int AvailableToHQ(', 'protected ERR_HunterVehicle ReserveSource(', 'protected void CommitReserve(')) run(r''' #include #include #include #include using string=std::string; templatestring Format(string s,T...){return s;} struct ERR_HQWatchEntry{bool Closed=false;int Fit=3;float ObservedAt=-1;}; struct Watch{std::map entries;ERR_HQWatchEntry* Find(string s){return entries[s];}}; struct ERR_HQ{struct Watch watch;struct Watch* Watch(){return &watch;}static void Event(string,string){}}; struct ERR_HunterVehicle{bool Motorbike=false,Sarka=false,ReserveCovered=false,Released=false;float LaunchedAt=0;string Source;}; struct ERR_HunterFleetRules{ '''+rules+r''' }; struct Service{std::vector m_Hunters;int m_Target=3,m_ReserveSpent=0; float m_NextReserve=0;ERR_HunterVehicle* m_PendingReserve=nullptr; '''+methods+r''' }; int main(){ ERR_HQWatchEntry report; assert(!ERR_HunterFleetRules::Unavailable(nullptr,100,480099)); assert(ERR_HunterFleetRules::Unavailable(nullptr,100,480100)); report.ObservedAt=100000;assert(!ERR_HunterFleetRules::Unavailable(&report,100,579999)); assert(ERR_HunterFleetRules::Unavailable(&report,100,580000)); report.Fit=0;assert(ERR_HunterFleetRules::Unavailable(&report,100,101)); report.Fit=3;report.Closed=true;assert(ERR_HunterFleetRules::Unavailable(&report,100,101)); assert(!ERR_HunterFleetRules::Unavailable(&report,100,99)); assert(!ERR_HunterFleetRules::ReserveDue(3,3,0,2,0,480000)); assert(!ERR_HunterFleetRules::ReserveDue(3,4,0,0,0,480000)); // aggression deployment first assert(!ERR_HunterFleetRules::ReserveDue(15,12,3,0,0,480000)); assert(!ERR_HunterFleetRules::ReserveDue(15,15,0,0,0,480000)); assert(!ERR_HunterFleetRules::ReserveDue(6,3,3,0,0,480000)); assert(!ERR_HunterFleetRules::ReserveDue(4,3,1,0,960000,959999)); assert(ERR_HunterFleetRules::ReserveDue(4,3,1,1,960000,960000)); Service s;ERR_HQ hq;ERR_HunterVehicle a,b,c,bike,sarka;a.Source="a";b.Source="b";c.Source="c"; bike.Source="bike";bike.Motorbike=true;sarka.Sarka=true;sarka.Source="sarka";s.m_Hunters={&sarka,&a,&b,&c,&bike};assert(s.Deployed()==3); a.Released=true;assert(s.AvailableToHQ(&hq,100)==3); // physical loss alone reveals nothing assert(!s.ReserveSource(&hq,479999)); assert(s.ReserveSource(&hq,480000)==&a); // absence of a first check-in has a bounded deadline report.Closed=false;report.ObservedAt=480000;hq.watch.entries["a"]=&report;hq.watch.entries["b"]=&report; assert(!s.ReserveSource(&hq,480000)); // fresh returning radios avert replacement report.Fit=0;assert(s.ReserveSource(&hq,480000)==&a); // delivered unfit crew can unlock reserve hq.watch.entries.clear(); s.m_PendingReserve=s.ReserveSource(&hq,480000); assert(s.m_ReserveSpent==0&&!a.ReserveCovered); // choosing/placement deferral does not spend ERR_HunterVehicle replacement;replacement.Source="replacement";replacement.LaunchedAt=480000; s.m_Hunters.push_back(&replacement);s.CommitReserve(&replacement,480000); assert(s.m_ReserveSpent==1&&a.ReserveCovered&&s.m_NextReserve==960000&&!s.m_PendingReserve); s.CommitReserve(&replacement,480001);assert(s.m_ReserveSpent==1); // one commit per departure attempt assert(!s.ReserveSource(&hq,959999));assert(s.ReserveSource(&hq,960000)==&b); b.ReserveCovered=true;c.ReserveCovered=true;replacement.ReserveCovered=true; assert(!s.ReserveSource(&hq,960000)); // no re-use of a covered loss, bikes cannot unlock reserves // A prolonged radio blackout admits exactly three reserves, eight minutes apart, then stops. Service outage;ERR_HQ silent;ERR_HunterVehicle originals[3],reserves[3]; for(int i=0;i<3;i++){originals[i].Source="original"+std::to_string(i);outage.m_Hunters.push_back(&originals[i]);} for(int i=0;i<3;i++){ float now=(i+1)*480000;assert(!outage.ReserveSource(&silent,now-1)); outage.m_PendingReserve=outage.ReserveSource(&silent,now);assert(outage.m_PendingReserve); reserves[i].Source="reserve"+std::to_string(i);reserves[i].LaunchedAt=now; outage.m_Hunters.push_back(&reserves[i]);outage.CommitReserve(&reserves[i],now); } assert(outage.Deployed()==6&&!outage.ReserveSource(&silent,9999999)); outage.m_Target=4;assert(!outage.ReserveSource(&silent,9999999)); // next ordinary escalation remains separate } ''') spawn = body(p, 'protected bool SpawnHunter(') assert spawn.index('ERR_EnemyLoadoutCatalog.Available') < spawn.index('CommitReserve(unit, now)') assert spawn.index('m_Hunters.Insert(unit)') < spawn.index('CommitReserve(unit, now)') < spawn.index('unit.Group =') print('PASS: reserve uses HQ reports/dispatch deadlines, preserves radio uncertainty, spends once, respects cooldown/availability/three-reserve/15-vehicle caps')