Integrate delivered HQ memory, finite physical convoy and RHS WCS dependencies

This commit is contained in:
2026-09-20 02:55:30 +02:00
parent e7b4141f69
commit a2aab17171
230 changed files with 37976 additions and 139 deletions
+224
View File
@@ -0,0 +1,224 @@
"""Execute actual bounded force ledger and dormant route bodies without engine claims."""
from policy_harness import ROOT, body, run
import re
L=ROOT/'addons/core/Scripts/Game/ERR/ERR_ForceLedger.c'
S=ROOT/'addons/core/Scripts/Game/ERR/ERR_ForceSimulation.c'
def cpp(s):
for old,new in [('protected ',''),('static ', 'static '),('Math.','Math::'),('vector.','vector::'),
('ERR_ForceRules.','ERR_ForceRules::'),('ERR_ForceKind.','ERR_ForceKind::'),
('ERR_ForceDuty.','ERR_ForceDuty::'),('Replication.','Replication::'),('out bool ','bool& '),
('sourceId.IsEmpty()','sourceId.empty()'),('sourceId.Length()','sourceId.size()'),
('string.Format(', 'Format('),('null','nullptr')]: s=s.replace(old,new)
for typ in ('ERR_ForceRecord','ERR_ForceRoute','ERR_ForceLedger','SCR_AIGroup'):
s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s)
for name in ('record','known','route','m_Ledger'):
s=s.replace(name+'.',name+'->')
s=s.replace('foreach (ERR_ForceRecord* record : m_Records)','for (ERR_ForceRecord* record : m_Records)')
s=s.replace('float length;','float length=0;').replace('int visited;','int visited=0;')
s=s.replace('int physical, dormant, eliminated, living, casualties;',
'int physical=0, dormant=0, eliminated=0, living=0, casualties=0;')
return s
rules='\n'.join(cpp(body(S,s)) for s in ('static bool Finite(', 'static bool FiniteVector(', 'static float Speed(',
'static int Advance(', 'static vector Step(', 'static bool Due(', 'static float Heading('))
route='\n'.join(cpp(body(L,s)) for s in ('int Count()', 'vector At(', 'bool Looped(', 'float Length(', 'bool Add(', 'bool Seal('))
ledger_source=L.read_text().split('class ERR_ForceLedger')[1]
# body() locates repeated Count/At unambiguously by extracting the ledger tail.
import tempfile
with tempfile.NamedTemporaryFile(mode='w',suffix='.c') as tail:
tail.write(ledger_source);tail.flush()
from pathlib import Path
ledger='\n'.join(cpp(body(Path(tail.name),s)) for s in ('int Count()', 'ERR_ForceRecord Find(',
'ERR_ForceRecord FindGroup(', 'ERR_ForceRecord At(', 'ERR_ForceRecord Register(', 'bool Unregister(',
'bool Eliminate(', 'bool RecordPhysicalState(', 'string Status(', 'void Clear('))
simulation='\n'.join(cpp(body(S,s)) for s in ('protected void Route(', 'void Tick('))
run(r'''
#include <cassert>
#include <cmath>
#include <algorithm>
#include <limits>
#include <map>
#include <vector>
#include <string>
#include <sstream>
using string=std::string;
struct vector{
float x=0,y=0,z=0;
vector()=default;vector(float a,float b,float c):x(a),y(b),z(c){}
vector(const char* value){std::istringstream(value)>>x>>y>>z;}
float& operator[](int i){return i==0?x:i==1?y:z;}
float operator[](int i)const{return i==0?x:i==1?y:z;}
vector operator+(vector b){return {x+b.x,y+b.y,z+b.z};}
vector operator-(vector b){return {x-b.x,y-b.y,z-b.z};}
vector operator*(float n){return {x*n,y*n,z*n};}
static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}
};
struct Math{static float Floor(float n){return std::floor(n);}
static float Atan2(float a,float b){return std::atan2(a,b);}
static constexpr float RAD2DEG=180/3.14159265358979323846;
};
struct Replication{inline static bool server=true;static bool IsServer(){return server;}};
int logs=0;void Print(const char*){logs++;}
string Format(const char*,int a,int b,int c,int d,int e,int f){
return std::to_string(a)+","+std::to_string(b)+","+std::to_string(c)+","+
std::to_string(d)+","+std::to_string(e)+","+std::to_string(f);
}
template<class T>struct Array:std::vector<T>{
int Count(){return this->size();}bool IsEmpty(){return this->empty();}
void Insert(T n){this->push_back(n);}void Clear(){this->clear();}
void RemoveItem(T n){this->erase(std::remove(this->begin(),this->end(),n),this->end());}
};
template<class K,class V>struct Map:std::map<K,V>{
bool Contains(K k){return this->count(k);}V Get(K k){return this->count(k)?this->at(k):V{};}
int Count(){return this->size();}void Set(K k,V v){(*this)[k]=v;}
void Remove(K k){this->erase(k);}void Clear(){this->clear();}
};
enum class ERR_ForceKind { GARRISON, PATROL, CHECKPOINT, CONVOY };
enum class ERR_ForceDuty { HOLD, PATROL_ROUTE, MOVE_TO, SEARCH, RETURN };
struct ERR_ForceRules{
'''+rules+r'''
};
struct ERR_ForceRoute{
Array<vector> m_Points;bool m_Loop=false,m_Sealed=false;float m_Length=0;
ERR_ForceRoute(bool loop=false):m_Loop(loop){}
'''+route+r'''
};
struct SCR_AIGroup{};
struct ERR_ForceRecord{
int Id=0;string SourceId;ERR_ForceKind Kind=ERR_ForceKind::GARRISON;
vector Position,HomePosition,DutyTarget;float Heading=0;
ERR_ForceDuty HomeDuty=ERR_ForceDuty::HOLD,Duty=ERR_ForceDuty::HOLD;
ERR_ForceRoute* Route=nullptr;int RouteIndex=0;float RouteProgress=0;bool RouteFinished=false;
int InitialStrength=0,Strength=0,Casualties=0,Importance=1,RadioCapability=0;
SCR_AIGroup* Group=nullptr;bool Physical=false,Pinned=false,Eliminated=false;
float LastSimulatedAt=0,StateSince=0;
};
struct ERR_ForceLedger{
Array<ERR_ForceRecord*>m_Records;Map<int,ERR_ForceRecord*>m_ById;
Map<string,int>m_Sources;Map<SCR_AIGroup*,int>m_Groups;
int m_NextId=0;bool m_CapLogged=false;
'''+ledger+r'''
};
struct ERR_ForceSimulation{
ERR_ForceLedger* m_Ledger;ERR_ForceSimulation(ERR_ForceLedger* ledger):m_Ledger(ledger){}
'''+simulation+r'''
};
bool near(float a,float b,float e=.005){return std::abs(a-b)<=e;}
ERR_ForceRoute* square(bool loop){
auto r=new ERR_ForceRoute(loop);
assert(r->Add({0,10,0})&&r->Add({14,12,0})&&r->Add({14,14,14})&&r->Add({0,16,14}));
return r;
}
int main(){
using K=ERR_ForceKind;using D=ERR_ForceDuty;using R=ERR_ForceRules;
const float nan=std::numeric_limits<float>::quiet_NaN();
assert(R::Speed(K::GARRISON,D::HOLD)==0);
assert(R::Speed(K::CONVOY,D::MOVE_TO)==11&&near(R::Speed(K::PATROL,D::PATROL_ROUTE),1.4));
assert(R::Speed(static_cast<K>(-1),D::SEARCH)==0);
assert(R::Advance(3,4,false,true)==4&&R::Advance(4,4,false,true)==4);
assert(R::Advance(3,4,true,true)==0&&R::Advance(2,4,true,false)==2);
assert(R::Advance(-1,4,true,true)==-1&&R::Advance(0,65,true,true)==-1);
bool arrived=false;auto p=R::Step({0,10,0},{14,20,0},1.4,5,arrived);
assert(!arrived&&near(p.x,7)&&near(p.y,15));
p=R::Step(p,{14,20,0},1.4,999,arrived);assert(arrived&&p.x==14&&p.y==20);
p=R::Step({0,10,0},{14,20,0},-1,2,arrived);assert(!arrived&&p.x==0);
p=R::Step({0,10,0},{14,20,0},nan,2,arrived);assert(!arrived&&p.x==0);
int due=0;for(int id=0;id<8;id++)due+=R::Due(id,1000,0,2000);
assert(due>0&&due<8&&!R::Due(0,1000,2000,2000)&&!R::Due(0,nan,0,2000));
assert(near(R::Heading({0,0,0},{1,0,0},0),90));
assert(near(R::Heading({0,0,0},{-1,0,0},0),270));
ERR_ForceRoute empty;assert(!empty.Seal());
ERR_ForceRoute zero(true);zero.Add({0,0,0});zero.Add({0,0,0});assert(!zero.Seal());
ERR_ForceRoute cap;for(int i=0;i<64;i++)assert(cap.Add({float(i),0,0}));
assert(!cap.Add({0,0,0})&&cap.Seal()&&!cap.Add({0,0,0}));
ERR_ForceRoute invalid;assert(!invalid.Add({nan,0,0}));
ERR_ForceLedger ledger;
Replication::server=false;assert(!ledger.Register("client",K::PATROL,{0,0,0},4));
Replication::server=true;
assert(!ledger.Register("",K::PATROL,{0,0,0},4));
assert(!ledger.Register("bad",K::PATROL,{nan,0,0},4));
assert(!ledger.Register("bad",K::PATROL,{0,0,0},0));
assert(!ledger.Register("bad",K::PATROL,{0,0,0},33));
assert(!ledger.Register("bad",K::PATROL,{0,0,0},4,nullptr,nullptr,-1));
auto loop=square(true);auto force=ledger.Register("loop",K::PATROL,{0,10,0},4,loop);
assert(force&&loop->Length()==56&&loop->m_Sealed&&force->HomeDuty==D::PATROL_ROUTE);
assert(ledger.Register("loop",K::PATROL,{999,0,0},32,loop)==force&&force->Strength==4);
assert(!ledger.Register("loop",K::CONVOY,{0,0,0},4,loop));
ERR_ForceSimulation sim(&ledger);
// Four legs at 1.4 m/s: return after 40 seconds, with authored elevation along each segment.
for(int t=2000;t<=40000;t+=2000){
sim.Tick(t);
if(t==10000)assert(near(force->Position.x,14)&&near(force->Position.z,0));
if(t==20000)assert(near(force->Position.x,14)&&near(force->Position.z,14));
if(t==30000)assert(near(force->Position.x,0)&&near(force->Position.z,14));
}
assert(vector::DistanceXZ(force->Position,{0,10,0})<.01&&near(force->Position.y,10));
// Huge elapsed distance skips complete laps instead of looping per travelled metre.
sim.Tick(40004000); // total 1,000 laps plus one 4-second fragment
assert(near(force->Position.x,5.6,.02)&&near(force->Position.z,0));
auto line=square(false);auto terminal=ledger.Register("line",K::PATROL,{0,10,0},4,line,nullptr,40004000);
sim.Tick(40044000);assert(terminal->RouteFinished&&terminal->RouteIndex==4&&terminal->Duty==D::HOLD);
assert(near(terminal->Position.x,0)&&near(terminal->Position.z,14)&&terminal->Position.y==16);
sim.Tick(40046000);assert(terminal->RouteIndex==4&&terminal->Position.z==14);
// Movement orders clamp to destination; HOLD does not continue extrapolating.
auto mover=ledger.Register("move",K::CONVOY,{0,0,0},4,nullptr,nullptr,40046000);
mover->Duty=D::MOVE_TO;mover->DutyTarget={22,4,0};sim.Tick(40048000);
assert(mover->Position.x==22&&mover->Position.y==4&&mover->Duty==D::HOLD);
// Physical snapshots preserve losses and body position, including native dormant transitions.
SCR_AIGroup group;
auto physical=ledger.Register("physical",K::GARRISON,{10,0,10},4,nullptr,&group,40048000);
assert(physical&&ledger.FindGroup(&group)==physical);
assert(!ledger.Register("duplicate-group",K::GARRISON,{10,0,10},4,nullptr,&group));
physical->Duty=D::MOVE_TO;physical->DutyTarget={100,0,100};sim.Tick(40050000);
assert(physical->Position.x==10&&physical->LastSimulatedAt==40048000);
assert(ledger.RecordPhysicalState(physical->Id,{20,3,20},45,3,40050000));
assert(physical->Strength==3&&physical->Casualties==1&&physical->Position.x==20);
assert(!ledger.RecordPhysicalState(physical->Id,{30,3,30},45,4,40052000));
assert(!ledger.RecordPhysicalState(physical->Id,{30,3,30},45,3,40049990));
assert(ledger.RecordPhysicalState(physical->Id,{20,3,20},45,3,40052000,false));
assert(!physical->Physical&&physical->Group==&group&&physical->StateSince==40052000);
sim.Tick(40054000);assert(physical->Position.x>20&&physical->Casualties==1);
assert(ledger.RecordPhysicalState(physical->Id,physical->Position,45,3,40054000,true));
assert(physical->Strength==3&&physical->Physical);
assert(ledger.RecordPhysicalState(physical->Id,{30,0,30},0,0,40056000));
assert(physical->Eliminated&&physical->Strength==0&&physical->Casualties==4);
assert(!ledger.RecordPhysicalState(physical->Id,{30,0,30},0,3,40058000));
assert(ledger.Eliminate(physical->Id,40058000));sim.Tick(40060000);assert(physical->Position.x==30);
Replication::server=false;
assert(!ledger.Eliminate(force->Id,40060000)&&!ledger.Unregister(force->Id));
auto before=force->Position;sim.Tick(40062000);assert(force->Position.x==before.x);
Replication::server=true;
assert(ledger.Unregister(physical->Id)&&!ledger.FindGroup(&group));
assert(!ledger.Register("physical",K::GARRISON,{0,0,0},4));
ledger.Clear();assert(ledger.Count()==0&&ledger.m_Sources.Count()==0&&ledger.m_ById.Count()==0);
// All 128 formations remain bounded through a simulated hour; stagger only affects snapshot age.
ERR_ForceLedger hour;ERR_ForceSimulation hourSim(&hour);auto circuit=square(true);
for(int i=0;i<128;i++)assert(hour.Register(std::to_string(i),K::PATROL,{0,10,0},4,circuit));
assert(!hour.Register("overflow",K::PATROL,{0,0,0},4));
assert(!hour.Register("overflow2",K::PATROL,{0,0,0},4)&&logs==1);
for(int t=1000;t<=3600000;t+=1000)hourSim.Tick(t);
assert(hour.Count()==128&&hour.m_Sources.Count()==128&&hour.m_ById.Count()==128);
for(int i=0;i<128;i++){
auto r=hour.At(i);assert(!r->Eliminated&&r->Strength==4&&r->Casualties==0);
assert(3600000-r->LastSimulatedAt<=1000);
float distance=std::fmod(1.4*r->LastSimulatedAt/1000,56);
float actual=0;
if(r->Position.z<.01)actual=r->Position.x;
else if(r->Position.x>13.99)actual=14+r->Position.z;
else if(r->Position.z>13.99)actual=42-r->Position.x;
else actual=56-r->Position.z;
assert(std::min(std::abs(actual-distance),56-std::abs(actual-distance))<.1);
}
assert(hour.Status()=="128,0,128,0,512,0");
assert(hour.Unregister(hour.At(0)->Id));
assert(!hour.Register("replacement",K::PATROL,{0,0,0},4)); // retired identity consumes finite pool
}
''')
for path in (L,S):
text=path.read_text()
for forbidden in ('ERR_HQ', 'GetGame(', 'GetOrigin(', 'GetPlayers(', 'GetControlledEntity(',
'Ground(', 'Spawn(', 'CallLater(', 'EOnFrame(', 'GetWorld(', 'GetAgents('):
assert forbidden not in text,(path,forbidden)
assert all(len(line)<120 for line in text.splitlines()),path
for signature in ('protected void Route(', 'void Tick('):
assert 'new ' not in body(S,signature)
print('PASS: actual force ledger, finite casualties/identity/cap, routes/arrival/elevation/overshoot, server and physical guards, staggered 128-record simulated hour without world queries')