"""Actual local handoff predicate/history/network route/origin delivery state machine.""" from pathlib import Path import os,re from policy_harness import ROOT,body,run network=Path(os.environ.get('ERR_NETWORK_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQNetwork.c')) helper=Path(os.environ.get('ERR_HANDOFF_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQLocalHandoff.c')) # Reuse the existing native graph syntax translation, not its policy doubles. base=Path(os.environ.get('ERR_NETWORK_HARNESS',ROOT/'tests/hq_network_regression.py')).read_text() ns={'__file__':str(ROOT/'tests/hq_network_regression.py')} exec(base[:base.index("headers=r'''")],ns) base_trans=ns['trans'] def trans(s): s=base_trans(s).replace('ref ','') s=s.replace('ERR_GameMode.Get()', 'ERR_GameMode::Get()').replace('ERR_EnemyEquipmentRules.','ERR_EnemyEquipmentRules::').replace('ERR_ForceRules.','ERR_ForceRules::').replace('ERR_HQLocalHandoff.','ERR_HQLocalHandoff::') s=re.sub(r'\bERR_GameMode (\w+)',r'ERR_GameMode* \1',s) s=re.sub(r'\bERR_HQStatusSampler (\w+)',r'ERR_HQStatusSampler* \1',s) s=s.replace('array busy','Arr* busy').replace('array','Arr').replace('array','Arr') s=s.replace('foreach (ERR_HQRadioNode* node : m_Nodes)','for (ERR_HQRadioNode* node : m_Nodes)') for name in ('roster','receiver','witness','busy'): s=s.replace(name+'.',name+'->') s=s.replace('receiver->Carrier.GetOrigin()', 'receiver->Carrier->GetOrigin()') return s headers=r''' #include #include #include #include #include #include #include struct vector{float v[3]{};float operator[](int i)const{return v[i];}static float DistanceXZ(vector a,vector b){return std::hypot(a[0]-b[0],a[2]-b[2]);}static float Distance(vector a,vector b){return std::sqrt(std::pow(a[0]-b[0],2)+std::pow(a[1]-b[1],2)+std::pow(a[2]-b[2],2));}}; vector V(float x,float z=0,float y=0){return {{x,y,z}};} namespace Math{int Floor(float x){return std::floor(x);}} std::string Format(const char*,int x,int z){return std::to_string(x)+":"+std::to_string(z);} templatestruct Arr:std::vector{using std::vector::vector;using std::vector::operator=;int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Clear(){this->clear();}void Insert(T x){this->push_back(x);}void InsertAt(T x,int i){this->insert(this->begin()+i,x);}void Remove(int i){this->erase(this->begin()+i);}int Find(T x){auto it=std::find(this->begin(),this->end(),x);return it==this->end()?-1:int(it-this->begin());}bool Contains(T x){return Find(x)>=0;}}; templatestruct Map:std::map{void Clear(){this->clear();}bool Contains(K k){return this->count(k);}bool Find(K k,V& v){if(!Contains(k))return false;v=this->at(k);return true;}V Get(K k){if(!Contains(k))return {};return this->at(k);}void Insert(K k,V v){(*this)[k]=v;}}; struct SCR_AIGroup{bool enemy=true,military=true;int players=0;}; struct IEntity{vector pos;bool alive=true,captured=false,player=false;int radio=0;SCR_AIGroup* group=nullptr;vector GetOrigin(){return pos;}}; struct ERR_GameMode{static ERR_GameMode*Get(){static ERR_GameMode m;return &m;}}; struct ERR_EnemyEquipmentRules{static bool Eligible(ERR_GameMode* m,SCR_AIGroup* g,IEntity* a){return m&&g&&g->enemy&&g->military&&g->players==0&&a&&a->alive&&!a->captured&&!a->player&&a->group==g;}}; struct ERR_ForceRules{static bool Finite(float x){return std::isfinite(x);}}; struct ERR_HQRadioNode{IEntity* Carrier{};SCR_AIGroup* Group{};int UnitId{},Capability{};bool Valid(bool manpack=false){return ERR_EnemyEquipmentRules::Eligible(ERR_GameMode::Get(),Group,Carrier)&&Carrier->radio>=(manpack?2:1);}std::string Label(){return "unit "+std::to_string(UnitId);}}; struct ERR_HQStatusSampler{SCR_AIGroup*group=nullptr;Arrmembers;bool attributed=true;bool OwnsRoster(SCR_AIGroup*g){return group&&group==g;}int InitialStrength(){return members.Count();}IEntity*InitialMember(int i){return members[i];}bool CanAttribute(int i,IEntity*a){return attributed&&members[i]==a;}}; struct ERR_HQJournal{Arr events;void Write(std::string kind,std::string text,int){events.Insert(kind+" "+text);}}; ''' helper_code=trans(helper.read_text()).replace('class ERR_HQLocalHandoff','struct ERR_HQLocalHandoff').replace('class ERR_HQObservationHistory','struct ERR_HQObservationHistory').replace('\n}\n','\n};\n') route_fields='SCR_AIGroup* LocalSource=nullptr;IEntity* LocalWitness=nullptr;ERR_HQStatusSampler* LocalRoster=nullptr;Arr Nodes;bool Downlink{},Sending{},Failed{},Originated{};int Cursor{},MessageId{};float Due{},StartedAt=-1,OriginDue=-1;vector SentFrom;std::string Payload;' route='struct ERR_HQRoute{'+route_fields+'\n'+'\n'.join(trans(body(network,x)) for x in ['int Advance(', 'protected string SenderLabel(', 'protected int Fail('])+'};\n' network_fields='int m_LocalChecks=0;Arr m_Nodes;Arr m_Parents,m_Depths;Map m_Indices;Map*> m_Cells;bool Overflow{};' graph='struct Network{'+network_fields+'\n'+'\n'.join(trans(body(network,x)) for x in ['void Clear(', 'protected string Cell(', 'void Add(', 'void Build(', 'ERR_HQRoute Route(', 'ERR_HQRoute LocalRoute('])+'};\n' run(headers+helper_code+route+graph+r''' struct Fixture{SCR_AIGroup source,receiver;IEntity witness,caller,other;ERR_HQStatusSampler roster;Network network;Arrbusy; Fixture(){witness.group=&source;caller.group=&receiver;caller.radio=2;caller.pos=V(100);roster.group=&source;roster.members={&witness};network.Add(&caller,&receiver,2,2);network.Build();} ERR_HQRoute* Local(){return network.LocalRoute(&source,&roster,&busy);} }; int main(){ {Fixture f;auto route=f.Local();assert(route&&route->Nodes[0]->Carrier==&f.caller&&route->LocalSource==&f.source&&route->LocalWitness==&f.witness&&route->LocalRoster==&f.roster); ERR_HQJournal journal;route->MessageId=17;route->Payload="unchanged sighting";assert(route->Advance(0,&journal)==0&&!route->Originated&&!route->Sending); assert(route->Advance(2999,&journal)==0&&!route->Originated);assert(route->Advance(3000,&journal)==0&&route->Originated&&route->Sending); // Local information is already handed over; later witness loss cannot erase it. f.witness.alive=false;assert(route->Advance(5000,&journal)==1&&route->MessageId==17&&route->Payload=="unchanged sighting");} for(int reason=0;reason<13;reason++){ Fixture f;auto r=f.Local();ERR_HQJournal j;assert(r&&r->Advance(0,&j)==0); if(reason==0){f.witness.alive=false;}if(reason==1){f.witness.group=&f.receiver;} if(reason==2){f.witness.player=true;}if(reason==3){f.roster.attributed=false;} if(reason==4){f.caller.radio=0;}if(reason==5){f.caller.alive=false;} if(reason==6){f.caller.captured=true;}if(reason==7){f.caller.group=&f.source;} if(reason==8){f.caller.pos=V(100.01f);}if(reason==9){f.caller.pos=V(0,0,100.01f);} if(reason==10){r->LocalSource=nullptr;}if(reason==11){r->LocalWitness=nullptr;} if(reason==12){f.source.players=1;} assert(r->Advance(1000,&j)==-1&&r->Failed&&!r->Originated&&!r->Sending);assert(r->Advance(5000,&j)==-1); } // Never trade an invalid first member for an unowned replacement; a different original may hand over. {Fixture f;f.roster.members[0]=&f.other;assert(!f.Local());f.other.group=&f.source;f.roster.members.Insert(&f.witness);assert(f.Local());} {Fixture f;f.busy.Insert(&f.caller);assert(!f.Local());f.busy.Clear();assert(f.Local());} {Fixture f;f.caller.radio=1;f.network.Clear();f.network.Add(&f.caller,&f.receiver,2,1);f.network.Build();assert(!f.Local());} {Fixture f;f.roster.members.resize(33,&f.witness);assert(!f.Local());} {Fixture f;f.network.m_LocalChecks=4096;assert(!f.Local());f.network.Clear();f.network.Add(&f.caller,&f.receiver,2,2);f.network.Build();assert(f.Local());} {Fixture f;f.network.Overflow=true;assert(!f.Local());} // Direct routes remain unaffected and final radio loss is still terminal after local completion. {Fixture f;auto direct=f.network.Route(&f.caller);f.witness.alive=false;ERR_HQJournal j;assert(direct->Advance(0,&j)==0);assert(direct->Advance(3000,&j)==0);assert(direct->Advance(5000,&j)==1);} {Fixture f;auto r=f.Local();ERR_HQJournal j;r->Advance(0,&j);r->Advance(3000,&j);f.caller.radio=0;assert(r->Advance(5000,&j)==-1);} // Per-source watermark rejects duplicate/replayed/future/stale sightings including failed-route attempts. {ERR_HQObservationHistory history;IEntity target;assert(history.CanQueue(&target,100,100));history.Queued(&target,100,100); assert(!history.CanQueue(&target,100,101)&&!history.CanQueue(&target,99,101));assert(history.CanQueue(&target,101,101)); assert(!history.CanQueue(&target,102,101)&&!history.CanQueue(&target,101,602)); assert(!history.CanQueue(&target,std::numeric_limits::quiet_NaN(),602));} {ERR_HQObservationHistory history;IEntity targets[65];for(int i=0;i<64;i++){history.Queued(&targets[i],100,100);}assert(!history.CanQueue(&targets[64],100,100)); assert(history.CanQueue(&targets[64],120101,120101));assert(!history.CanQueue(&targets[0],100,120101));} } ''') print('PASS: actual original-roster local handoff,100m3D bound,busy carrier/budget exclusion,origin interruption including deletedsource,normal radio delivery and per-source replay history')