"""Execute actual finite convoy pool methods; mission/service APIs are controlled doubles.""" import os import re from pathlib import Path from policy_harness import ROOT, body, run P = Path(os.environ.get('ERR_INFANTRY_SUPPLY_POOL_SOURCE', ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_InfantrySupplyPool.c')) def cpp(source): source = source.replace('protected ', '').replace('null', 'nullptr') source = re.sub(r'foreach \((\w+) (\w+) : ([^)]+)\)', r'for (\1 \2 : \3)', source) for name in ('ERR_HQ', 'ERR_GameMode', 'ERR_ConvoyService', 'ERR_ConvoyTask', 'ERR_InfantryResupply'): source = re.sub(r'\b' + name + r' (?=\w)', name + '* ', source) for name in ('Replication', 'ERR_GameMode', 'ERR_ForceRules', 'Math'): source = source.replace(name + '.', name + '::') for name in ('m_HQ', 'm_Mode', 'service', 'retained', 'convoys'): source = source.replace(name + '.', name + '->') source = source.replace('int trucks, working, moved, stock, mines;', 'int trucks=0, working=0, moved=0, stock=0, mines=0;') source = source.replace('string.Format', 'Format') return source methods = '\n'.join(cpp(body(P, signature)) for signature in ( 'void ERR_InfantrySupplyPool(', 'int Count(', 'ERR_InfantryResupply At(', 'ERR_InfantryResupply First(', 'protected bool Known(', 'bool Unresolved(', 'void Tick(', 'string Status(', 'void Stop(')) methods = methods.replace('void ERR_InfantrySupplyPool(', 'Pool(') run(r''' #include #include #include #include #include #include using string=std::string; templatestruct Array:std::vector{int Count(){return this->size();}void Insert(T value){this->push_back(value);}}; templatestruct Map{std::mapvalues;void Set(string key,V value){values[key]=value;}V Get(string key){return values[key];}}; struct Replication{static inline bool server=true;static bool IsServer(){return server;}}; struct ERR_ForceRules{static bool Finite(float value){return std::isfinite(value);}}; struct Math{static int Min(int a,int b){return atasks;int reads=0;int Count(){return tasks.Count();}ERR_ConvoyTask* At(int index){reads++;assert(index>=0&&index* consumed=nullptr;Map* next=nullptr; int polls=0;bool unresolved=false;bool Unresolved(){return unresolved;}int slot=-1,binds=0,ticks=0,stopCalls=0,state=1,moved=0,stock=48;bool stopped=false; ERR_InfantryResupply(ERR_HQ*,ERR_GameMode*){made++;} bool Bind(ERR_ConvoyTask* t,int i,Map* c,Map* n){assert(!task);binds++;if(!bindAllowed)return false;task=t;slot=i;consumed=c;next=n;return true;} ERR_ConvoyTask* Task(){return task;} void Tick(float){polls++;if(!stopped)ticks++;} void Stop(){stopCalls++;stopped=true;} int mines=0;int MinesDelivered(){return mines;}int State(){return state;}int Delivered(){return moved;}int StockCount(){return stock;} }; struct Pool{ ERR_HQ* m_HQ=nullptr;ERR_GameMode* m_Mode=nullptr;Arraym_Services; Map consumed;Map next;Map* m_Consumed=&consumed;Map* m_NextAttempt=&next;bool m_Stopped=false; '''+methods+r''' }; struct Fixture{ ERR_GameMode mode;ERR_HQ hq;ERR_ConvoyService convoys;Pool pool;ERR_ConvoyTask tasks[25]; Fixture():pool(&hq,&mode){ERR_GameMode::current=&mode;Replication::server=true;hq.convoys=&convoys;} void Add(int n){for(int i=0;i::infinity(),std::numeric_limits::quiet_NaN()})f.pool.Tick(t); assert(f.pool.Count()==0);f.hq.convoys=nullptr;f.pool.Tick(0);assert(f.pool.Count()==0);} {ERR_GameMode mode;ERR_GameMode::current=&mode;Pool missingHQ(nullptr,&mode);missingHQ.Tick(0);assert(missingHQ.Count()==0); ERR_HQ hq;Pool missingMode(&hq,nullptr);missingMode.Tick(0);assert(missingMode.Count()==0);} // Empty discovery stays idle; later existing tasks bind independently exactly once. {Fixture f;f.pool.Tick(0);assert(f.pool.Count()==0&&!f.pool.First()&&!f.pool.At(-1)&&!f.pool.At(0)); f.Add(2);f.pool.Tick(1000);auto a=f.pool.At(0);auto b=f.pool.At(1); assert(f.pool.Count()==2&&f.pool.First()==a&&a!=b&&a->task==&f.tasks[0]&&b->task==&f.tasks[1]); assert(a->slot==0&&b->slot==1&&a->binds==1&&b->binds==1&&a->ticks==1&&b->ticks==1); f.pool.Tick(2000);assert(f.pool.At(0)==a&&f.pool.At(1)==b&&a->binds==1&&b->binds==1&&a->ticks==2&&b->ticks==2); // Delivered demand consumption/cooldown state has exact shared identity across trucks. assert(a->consumed==b->consumed&&a->next==b->next);a->consumed->Set("patrol",17);a->next->Set("patrol",180000); assert(b->consumed->Get("patrol")==17&&b->next->Get("patrol")==180000); a->state=3;a->moved=2;b->moved=4;b->stock=8;assert(f.pool.Status()=="2:1:6:56:0"); b->state=7;b->mines=1;assert(f.pool.Status()=="2:2:6:56:1"); f.pool.Stop();assert(a->stopped&&b->stopped&&a->stopCalls==1&&b->stopCalls==1);f.pool.Stop();f.Add(1);f.pool.Tick(3000); assert(f.pool.Count()==2&&a->ticks==2&&b->ticks==2&&a->stopCalls==1&&b->stopCalls==1);} // Stopped and detached originals still receive bounded proof polls, never new work. {Fixture f;f.Add(1);f.pool.Tick(0);auto a=f.pool.At(0);a->unresolved=true;assert(f.pool.Unresolved()); f.convoys.tasks[0]=&f.tasks[1];f.pool.Tick(1000);assert(a->stopped&&a->polls==2&&a->ticks==1&&f.pool.Unresolved()); f.pool.Stop();int made=ERR_InfantryResupply::made;f.pool.Tick(2000);assert(a->polls==3&&a->ticks==1&&ERR_InfantryResupply::made==made); a->unresolved=false;assert(!f.pool.Unresolved());} // A stopped empty pool can never create service work later. {Fixture f;f.pool.Stop();f.Add(2);f.pool.Tick(0);assert(f.pool.Count()==0);} // A refused binding remains a stopped terminal slot, never ticking or retrying fallback stock. {Fixture f;f.Add(1);ERR_InfantryResupply::bindAllowed=false;f.pool.Tick(0);auto rejected=f.pool.At(0); assert(rejected&&rejected->stopped&&rejected->binds==1&&rejected->ticks==0&&!rejected->task); ERR_InfantryResupply::bindAllowed=true;f.pool.Tick(1000); assert(f.pool.At(0)==rejected&&rejected->binds==1&&rejected->ticks==0&&!rejected->task);} // Maximum immutable slot span is twenty, even if malformed source offers more. {Fixture f;f.Add(25);int made=ERR_InfantryResupply::made;f.pool.Tick(0); assert(f.pool.Count()==20&&ERR_InfantryResupply::made==made+20&&f.convoys.reads==20&&!f.pool.At(20)); f.pool.Tick(1000);assert(f.pool.Count()==20&&ERR_InfantryResupply::made==made+20&&f.convoys.reads==40);} // Sparse source identities do not shift services or substitute a later donor for First(). {Fixture f;f.convoys.tasks.Insert(nullptr);f.convoys.tasks.Insert(&f.tasks[1]);f.pool.Tick(0); auto second=f.pool.At(1);assert(f.pool.Count()==2&&!f.pool.At(0)&&!f.pool.First()&&second&&second->slot==1); f.convoys.tasks[0]=&f.tasks[0];f.pool.Tick(1000);assert(f.pool.At(0)&&f.pool.First()==f.pool.At(0)&&f.pool.At(1)==second);} // Repeated source identity never creates duplicate stock ownership. {Fixture f;f.Add(1);f.convoys.tasks.Insert(&f.tasks[0]);f.pool.Tick(0);assert(f.pool.At(0)&&!f.pool.At(1)); f.pool.Tick(1000);assert(!f.pool.At(1));} // Replacing/removing a slot stops only its original service; no fresh stock is adopted. {Fixture f;f.Add(2);f.pool.Tick(0);auto a=f.pool.At(0);auto b=f.pool.At(1); f.convoys.tasks[0]=&f.tasks[2];f.pool.Tick(1000);assert(a->stopped&&a->task==&f.tasks[0]&&a->ticks==1&&b->ticks==2&&f.pool.At(0)==a); f.convoys.tasks[0]=&f.tasks[0];f.pool.Tick(2000);assert(a->ticks==1&&a->binds==1&&b->ticks==3); f.convoys.tasks.resize(1);f.pool.Tick(3000);assert(b->stopped&&f.pool.Count()==2&&f.pool.At(1)==b&&b->task==&f.tasks[1]);} } ''') print('PASS: actual finite convoy supply pool; immutable slots, shared demand state, sparse/capped discovery and permanent shutdown')