#!/usr/bin/env python3 """Execute production bounded radio graph and hop-delivery state machine.""" from pathlib import Path import re, subprocess, tempfile ROOT=Path(__file__).resolve().parents[1] source=(ROOT/'addons/ai/Scripts/Game/ERR/ERR_HQNetwork.c').read_text() def extract(signature): a=source.index(signature);b=source.index('{',a);end=b+1;depth=1 while depth:depth+=(source[end]=='{')-(source[end]=='}');end+=1 return source[a:end] def trans(s): s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('null','nullptr') s=s.replace('string.Format','Format').replace('string ', 'std::string ') s=s.replace('Math.Round(distance).ToString()', 'std::to_string(int(distance))') s=re.sub(r'\b(\w+)\.ToString\(\)',r'std::to_string(\1)',s) for name in ['Math','vector']:s=s.replace(name+'.',name+'::') for typ in ['IEntity','SCR_AIGroup','ERR_HQRoute','ERR_HQRadioNode','ERR_HQJournal']: s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s) s=s.replace('array queue = {}','Arr queue').replace('array cell','Arr* cell').replace('array neighbors','Arr* neighbors') s=s.replace('cell = {};','cell = new Arr();') s=re.sub(r'foreach\s*\(int (\w+) : neighbors\)',r'for (auto \1 : *neighbors)',s) for name in ['carrier','caller','node','route','journal','cell']: s=s.replace(name+'.',name+'->') s=re.sub(r'(Nodes|m_Nodes)\[([^\]]+)\]\.',r'\1[\2]->',s) s=s.replace('Carrier.GetOrigin()', 'Carrier->GetOrigin()') s=re.sub(r'\b(int|float|bool) (\w+);',r'\1 \2{};',s) 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]);}}; vector V(float x,float z=0){return {{x,0,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{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);}}; 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;};struct IEntity{vector pos;bool alive=true;int radio=1;bool captured=false;vector GetOrigin(){return pos;}}; struct ERR_HQRadioNode{IEntity* Carrier{};SCR_AIGroup* Group{};int UnitId{},Capability{};bool Valid(bool manpack=false){return Carrier&&Carrier->alive&&!Carrier->captured&&Carrier->radio>=(manpack?2:1);}std::string Label(){return "unit "+std::to_string(UnitId);}}; struct ERR_HQJournal{Arr events;void Write(std::string kind,std::string text,int){events.Insert(kind+" "+text);}}; ''' routefields='Arr Nodes;bool Downlink{},Sending{},Failed{};int Cursor{},MessageId{};float Due{},StartedAt=-1;vector SentFrom;std::string Payload;' networkfields='Arr m_Nodes;Arr m_Parents,m_Depths;Map m_Indices;Map*> m_Cells;bool Overflow{};' route='struct ERR_HQRoute{'+routefields+'\n'+'\n'.join(trans(extract(x)) for x in ['int Advance(', 'protected string SenderLabel(', 'protected int Fail('])+'};\n' network='struct Network{'+networkfields+'\n'+'\n'.join(trans(extract(x)) for x in ['void Clear(', 'protected string Cell(', 'void Add(', 'void Build(', 'ERR_HQRoute Route('])+'};\n' tests=r''' int main(){ Network network;SCR_AIGroup group;IEntity c[5];for(int i=0;i<5;i++)c[i].pos=V(i*500); c[0].radio=2;for(int i=0;i<5;i++)network.Add(&c[i],&group,37+i,c[i].radio);network.Build(); assert(network.Route(&c[0])->Nodes.Count()==1);assert(network.Route(&c[3])->Nodes.Count()==4);assert(!network.Route(&c[4])); ERR_HQJournal journal;auto up=network.Route(&c[2]);assert(up->Nodes.Count()==3); assert(up->Advance(0,&journal)==0);assert(up->Advance(1999,&journal)==0);assert(up->Advance(2000,&journal)==0); c[2].alive=false;assert(up->Advance(4000,&journal)==0);c[0].alive=false;assert(up->Advance(6000,&journal)==1); assert(journal.events.back().find("HQ received")!=std::string::npos); // cannot recall an emitted uplink for(auto& carrier:c)carrier.alive=true; auto lost=network.Route(&c[2]);assert(lost->Advance(0,&journal)==0);c[1].radio=0;assert(lost->Advance(2000,&journal)==-1);assert(lost->Advance(4000,&journal)==-1); c[1].radio=1;auto moving=network.Route(&c[2]);moving->Advance(0,&journal);c[1].pos=V(499);assert(moving->Advance(2000,&journal)==-1);c[1].pos=V(500); auto down=network.Route(&c[2],true);assert(down->Nodes[0]->Carrier==&c[0]);assert(down->Advance(0,&journal)==0); assert(down->Advance(2000,&journal)==0);assert(down->Advance(4000,&journal)==0);assert(down->Advance(6000,&journal)==1); auto captured=network.Route(&c[2],true);captured->Advance(0,&journal);c[0].captured=true;assert(captured->Advance(2000,&journal)==-1);c[0].captured=false; auto timeout=network.Route(&c[2]);timeout->Advance(0,&journal);assert(timeout->Advance(30001,&journal)==-1); Network disconnected;disconnected.Add(&c[1],&group,1,1);disconnected.Build();assert(!disconnected.Route(&c[1])); Network boundary;c[1].pos=V(501);boundary.Add(&c[0],&group,1,2);boundary.Add(&c[1],&group,2,1);boundary.Build();assert(!boundary.Route(&c[1])); network.Clear();assert(!network.Route(&c[0])); Network crowded;std::vector many(2050);for(auto& carrier:many){carrier.radio=2;crowded.Add(&carrier,&group,1,2);}assert(crowded.Overflow&&crowded.m_Nodes.Count()==2048); crowded.Build();assert(crowded.Overflow); // bounded dense graph work std::cout<<"PASS: real-carrier shortest-hop paths, 500m boundary, three-hop cap, no-manpack failure, hop timing, emitted-message survival, radio loss, capture, motion, timeout, reset and network bounds\n"; } ''' with tempfile.TemporaryDirectory(prefix='eai-hq-network-') as tmp: cpp=Path(tmp)/'test.cpp';exe=Path(tmp)/'test';cpp.write_text(headers+route+network+tests) subprocess.run(['g++','-std=c++17','-Wall','-Wextra',str(cpp),'-o',str(exe)],check=True) subprocess.run([str(exe)],check=True)