"""Execute the actual field policy equations; no claim about native stopping/flight.""" from policy_harness import ROOT, run import re source = (ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFieldRules.c').read_text() cpp = source.replace('class ERR_AircraftFieldRules', 'struct Rules').replace('out vector nextIntegral', 'vector& nextIntegral') cpp = cpp.replace('Math.', 'Math::').replace('vector.', 'vector::').replace('"0 0 0"', 'vector{}') cpp = re.sub(r'(? #include #include #include struct vector { float v[3]={0,0,0}; vector()=default; vector(float x,float y,float z):v{x,y,z}{} float& operator[](int i){return v[i];} float operator[](int i)const{return v[i];} vector operator+(vector b)const{return {v[0]+b[0],v[1]+b[1],v[2]+b[2]};} vector operator-(vector b)const{return {v[0]-b[0],v[1]-b[1],v[2]-b[2]};} vector operator*(float s)const{return {v[0]*s,v[1]*s,v[2]*s};} float Length()const{return std::sqrt(Dot(*this,*this));} static float Dot(vector a,vector b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} static float DistanceXZ(vector a,vector b){return std::hypot(a[0]-b[0],a[2]-b[2]);} }; struct Math {static float AbsFloat(float x){return std::abs(x);} static float Clamp(float x,float lo,float hi){return std::clamp(x,lo,hi);} static float Max(float a,float b){return std::max(a,b);} static float Min(float a,float b){return std::min(a,b);} static float Sqrt(float a){return std::sqrt(a);}}; '''+cpp+r''' bool close(float a,float b){return std::abs(a-b)<.00001f;} int main(){ assert(Rules::Length(99.95f)&&Rules::Length(100.05f)); assert(!Rules::Length(99.949f)&&!Rules::Length(100.051f)&&!Rules::Length(NAN)); assert(Rules::SpeedAllowed({1.5f,9,2})&&!Rules::SpeedAllowed({1.501f,0,2})); assert(!Rules::SpeedAllowed({0,NAN,0})&&!Rules::SpeedAllowed({INFINITY,0,0})); assert(Rules::ValidStep(.5f)&&!Rules::ValidStep(0)&&!Rules::ValidStep(.501f)&&!Rules::ValidStep(NAN)); float speed=0; for(int i=0;i<100;i++){float next=Rules::RampSpeed(speed,2,.1f);assert(next-speed<=.020001f);speed=next;} assert(close(speed,2)); for(int i=0;i<100;i++){float next=Rules::RampSpeed(speed,0,.1f);assert(next<=speed&&speed-next<=.020001f);speed=next;} assert(close(speed,0)); assert(close(Rules::RampSpeed(1,2,1),1)&&close(Rules::RampSpeed(1,NAN,.1f),1)); vector previous={2,0,0}; for(int i=0;i<400;i++){ vector ramped=Rules::RampReference(previous,{-2,8,0},.1f); assert((ramped-previous).Length()<=.020001f&&ramped.Length()<=2.00001f&&ramped[1]==0); previous=ramped; } assert(previous[0]<-1.99f); vector sideways=Rules::RampReference({2,0,0},{0,0,2},.1f); assert((sideways-vector{2,0,0}).Length()<=.020001f); assert(close(Rules::RampReference({1,4,0},{-2,0,0},0)[0],1)); assert(!Rules::ShouldBrake(100.23f,1)&&!Rules::ShouldBrake(108,2)&&!Rules::ShouldBrake(120,2)); assert(Rules::ShouldBrake(120.01f,1)&&!Rules::Length(108)); vector integral, next; for(int i=0;i<2000;i++){ vector demand=Rules::VelocityDemand({2,100,0},{0,-2,0},integral,.1f,next); assert(close(demand[0],.08f)&&demand[1]==0&&next.Length()==0);integral=next; } // Low error accumulates useful demand rather than permanently freezing I. integral={};vector demand=Rules::VelocityDemand({.1f,5,0},{0,7,0},integral,.5f,next); assert(close(next[0],.05f)&&close(demand[0],.011f)&&demand[1]==0); // Opposing error unwinds a positive stored integral. integral={4,0,0};demand=Rules::VelocityDemand({0,0,0},{.1f,0,0},integral,.5f,next); assert(next[0]<4&&demand[0]<.08f); // Diagonal demand is capped as a vector, not independently on both axes. demand=Rules::VelocityDemand({2,3,2},{-1,4,-1},{4,5,4},.1f,next); assert(demand.Length()<=.080001f&&demand[1]==0&&next.Length()<=4.00001f&&next[1]==0); demand=Rules::VelocityDemand({0,0,0},{2,0,0},{},.1f,next); assert(demand[0]<0&&close(demand.Length(),.08f)); demand=Rules::VelocityDemand({NAN,0,0},{},{1,0,0},.1f,next);assert(demand.Length()==0&&next.Length()==0); demand=Rules::VelocityDemand({1,0,0},{},{1,0,0},0,next);assert(demand.Length()==0&&next.Length()==0); assert(close(Rules::BrakingReserve(0),3)&&close(Rules::BrakingReserve(2.5f),26.333334f)); assert(Rules::ShouldBrake(3,0)&&!Rules::ShouldBrake(3.001f,0)&&!Rules::ShouldBrake(25,0)); for(float actualSpeed:{0.0f,.1f,.25f,1.0f,1.5f,2.0f,2.5f}){ float reserve=Rules::BrakingReserve(actualSpeed); assert(Rules::ShouldBrake(reserve,actualSpeed)&&!Rules::ShouldBrake(reserve+.001f,actualSpeed)); } // Stop reference never chases the destination or reverses direction. for(vector initial:{vector{2,0,0},vector{-1.2f,0,1.6f},vector{0,0,0}}){ vector v=initial; for(int i=0;i<110;i++){ vector nextV=Rules::RampReference(v,{},.1f); assert(nextV.Length()<=v.Length()+.000001f&&(nextV-v).Length()<=.020001f); assert(vector::Dot(nextV,initial)>=-.000001f);v=nextV; } assert(v.Length()<=.00001f); } assert(Rules::ShouldBrake(26,2.5f)&&!Rules::ShouldBrake(27,2.5f)); assert(Rules::ShouldBrake(50,NAN)&&Rules::ShouldBrake(NAN,1)); assert(Rules::LeaseAdmitted(1000,241000)&&Rules::LeaseAdmitted(1000,240000)); assert(!Rules::LeaseAdmitted(1000,239999)&&!Rules::LeaseAdmitted(1000,241001)&&!Rules::LeaseAdmitted(-1,239999)); assert(!Rules::AbortDue(154999,240000)&&Rules::AbortDue(155000,240000)); assert(Rules::AbortDue(240000,240000)&&Rules::AbortDue(NAN,240000)); assert(Rules::LowSpeedHeld(.249f,1000,3000)&&!Rules::LowSpeedHeld(.25f,1000,3000)); assert(!Rules::LowSpeedHeld(0,1000,2999)&&!Rules::LowSpeedHeld(0,-1,3000)&&!Rules::LowSpeedHeld(NAN,0,3000)); vector start={0,10,0},end={100,10,0}; assert(Rules::Contains(start,end,{50,18,7.999f},1)); assert(!Rules::Contains(start,end,{50,18,8},1)); assert(Rules::Contains(start,end,{-7.99f,18,0},1)&&!Rules::Contains(start,end,{-8,18,0},1)); assert(!Rules::Contains(start,end,{108,18,0},1)&&!Rules::Contains(start,end,{50,28.01f,0},1)); assert(!Rules::Contains(start,end,{50,6.99f,0},1)&&!Rules::Contains(start,end,{50,18,0},.84f)); assert(!Rules::Contains(start,end,{NAN,18,0},1)&&!Rules::Contains(start,end,{50,18,0},NAN)); } ''') print('PASS: actual field PI/ramp/vector caps, antiwindup, braking hypothesis, immutable admission limits and segment bounds; no native physics tested')