104 lines
7.8 KiB
Python
104 lines
7.8 KiB
Python
"""Actual field evidence/observation bodies; physical engine boundaries remain untested."""
|
|
from policy_harness import ROOT, body, run
|
|
import re
|
|
P=ROOT/'addons/ai/Scripts/Game/ERR/ERR_NativeAircraftFieldProbe.c'
|
|
s=P.read_text()
|
|
def cpp(x):
|
|
x=x.replace('ERR_AircraftFlightState.','State::').replace('ERR_NativeAircraftFieldRules.','Rules::').replace('ERR_AircraftFieldRules.','FieldRules::').replace('vector.','vector::').replace('ERR_AircraftFieldFlight flight','Flight* flight').replace('flight.','flight->')
|
|
return re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])',r'\1f',x)
|
|
rules=s[s.index('class ERR_NativeAircraftFieldRules'):s.index('class ERR_NativeAircraftFieldProbe')].replace('class ERR_NativeAircraftFieldRules','struct Rules').rstrip()+';'
|
|
observed='\n'.join(cpp(body(P,k)) for k in ('void BrakeBegan(', 'void BrakeStopped('))
|
|
run(r'''#include <cassert>
|
|
#include <cmath>
|
|
#include <string>
|
|
using string=std::string;
|
|
struct vector {float x=0,y=0,z=0;static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
|
|
namespace State {enum {COMPLETE=8,CANCELLED,FAILED,RELINQUISHED};}
|
|
'''+cpp(rules)+r'''
|
|
struct FieldRules {static float HorizontalSpeed(vector v){return std::hypot(v.x,v.z);}static float BrakingReserve(float s){return s*s/.30f+s+3;}};
|
|
struct Flight {float ERR_NativeFieldStageDeadline(){return 70000;}vector LandingPoint(){return {100,0,0};}};
|
|
template<typename...T>void PrintFormat(T...args){}
|
|
struct Probe {
|
|
bool m_Stopped=false,m_BrakeObserved=false,m_BrakeStopped=false,m_BrakePassed=false;
|
|
string m_ObservationFailure;
|
|
Flight* m_Flight=nullptr;float m_BrakeAt=0,m_BrakeReserve=0,m_BrakeStageDeadline=0,m_ControlDeadline=240000,m_BrakeSampledTravel=0,m_BrakeLowSince=-1;
|
|
vector m_BrakeOrigin,m_BrakePrevious,m_BrakeLandingPoint;
|
|
'''+observed+r'''
|
|
};
|
|
int main(){
|
|
assert(Rules::Cruise(1,1,false,1)&&!Rules::Cruise(.99f,1,false,1)&&!Rules::Cruise(1,2.501f,false,1));
|
|
assert(!Rules::Cruise(1,1,true,1)&&!Rules::Cruise(1,1,false,2));
|
|
assert(Rules::Terminal(false,State::COMPLETE,0,true,true,true,true,100,.5f));
|
|
assert(!Rules::Terminal(false,State::COMPLETE,0,true,false,true,true,100,.5f));
|
|
assert(!Rules::Terminal(false,State::COMPLETE,0,true,true,false,true,100,.5f));
|
|
assert(!Rules::Terminal(false,State::COMPLETE,0,true,true,true,false,100,.5f));
|
|
assert(!Rules::Terminal(false,State::COMPLETE,0,true,true,true,true,98,.5f));
|
|
assert(!Rules::Terminal(false,State::COMPLETE,0,true,true,true,true,100,.76f));
|
|
assert(Rules::Terminal(true,State::CANCELLED,1,false,false,false,true,30,.5f));
|
|
assert(!Rules::Terminal(true,State::CANCELLED,1,true,false,false,true,30,.5f));
|
|
assert(!Rules::Terminal(true,State::COMPLETE,1,false,false,false,true,30,.5f));
|
|
assert(!Rules::Terminal(true,State::CANCELLED,0,false,false,false,true,30,.5f));
|
|
assert(!Rules::Terminal(true,State::CANCELLED,2,false,false,false,true,30,.5f));
|
|
assert(Rules::PhysicalFinish(true,true,true,1,1,1,1,101,101));
|
|
assert(!Rules::PhysicalFinish(true,false,true,1,1,1,1,101,101));
|
|
assert(!Rules::PhysicalFinish(true,true,false,1,1,1,1,101,101));
|
|
assert(!Rules::PhysicalFinish(true,true,true,0,1,1,1,101,101));
|
|
assert(!Rules::PhysicalFinish(true,true,true,1,2,1,1,101,101));
|
|
for(int i=0;i<5;i++)assert(!Rules::CanDispose(i!=0,i!=1,i!=2,i!=3,i!=4));
|
|
Flight flight,foreign;Probe p;p.m_Flight=&flight;
|
|
p.BrakeBegan(&foreign,1000,{0,0,0},{2,0,0});assert(!p.m_BrakeObserved);
|
|
p.BrakeBegan(&flight,1000,{0,0,0},{2,0,0});assert(p.m_BrakeObserved&&p.m_BrakeReserve>18&&p.m_BrakeReserve<19);
|
|
p.m_BrakeSampledTravel=15;p.m_BrakePrevious={15,0,0};p.m_BrakeLowSince=2000;
|
|
p.BrakeStopped(&flight,4000,{16,0,0},{.1f,0,0});assert(p.m_BrakePassed&&p.m_BrakeStopped&&p.m_ObservationFailure.empty());
|
|
p.BrakeBegan(&flight,5000,{50,0,0},{2,0,0});assert(!p.m_ObservationFailure.empty()&&p.m_BrakeAt==1000); // no renewal
|
|
for(int fault=0;fault<4;fault++){
|
|
Probe bad;bad.m_Flight=&flight;bad.BrakeBegan(&flight,1000,{0,0,0},{2,0,0});bad.m_BrakeLowSince=2000;
|
|
float stopped=4000;vector at={16,0,0},vel={.1f,0,0};
|
|
if(fault==0){at={19,0,0};} if(fault==1){stopped=3999;} if(fault==2){vel={.25f,0,0};} if(fault==3){bad.m_BrakeSampledTravel=20;}
|
|
bad.BrakeStopped(&flight,stopped,at,vel);assert(!bad.m_BrakePassed&&!bad.m_ObservationFailure.empty()&&!bad.m_Stopped); // failure deferred outside callback
|
|
}
|
|
assert(!Rules::Braking(NAN,1,1,0,3000,0)&&!Rules::Braking(10,NAN,1,0,3000,0));
|
|
}
|
|
''')
|
|
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
|
|
assert 'outcome == "aircraft-field"' in body(P,'static bool Enabled(')
|
|
find=body(P,'protected bool FindPad(')
|
|
assert '"4850.953 0 11787.412"' in find and '"100 0 0"' in find
|
|
assert find.count('.Survey(')==1 and 'for (' not in find and 'AirportOffset' not in s
|
|
assert s.count('ERR_Actors.Spawn(AIRCRAFT,')==1 and s.count('SpawnCharacterInCompartment(')==1
|
|
assert s.count('physics.SetActive(ActiveState.ACTIVE)')==1
|
|
for prohibited in ('SetWorldTransform(', 'SetOrigin(', 'SetVelocity(', '.StartEngine(', '.StopEngine(', 'm_Mode.Own(', 'm_Flight.Tick(', 'm_Flight.Cancel(', 'm_Flight.StartField(', 'FieldDemand('):assert prohibited not in s
|
|
for sig in ('void PrepareActions(', 'void PrepareHelicopter('):
|
|
assert '++' in body(P,sig) and '.Prepare' not in body(P,sig)
|
|
for sig in ('void BrakeBegan(', 'void BrakeStopped('):assert 'Check(' not in body(P,sig) and 'Stop(' not in body(P,sig)
|
|
assert 'super.BeginFieldBrake(now)' in body(P,'protected override bool BeginFieldBrake(')
|
|
assert 'super.CheckTranslation(now, position, height, speed, upright)' in body(P,'protected override void CheckTranslation(')
|
|
start=body(P,'protected bool StartRetainedField(');assert '.StartField(m_Aircraft, m_Envelope, ORDER_TOKEN, m_ControlDeadline)' in start
|
|
assert 'ERR_AircraftFieldFlight.Cast(entry.Flight)' in body(P,' ERR_AircraftFieldFlight ERR_NativeOriginalFieldFlight(')
|
|
tick=body(P,'int Tick(')
|
|
assert tick.index('FindPad(m_Pad)')<tick.index('ERR_Actors.Spawn(AIRCRAFT,')
|
|
assert tick.index('QualifyField(')<tick.index('StartRetainedField()')
|
|
assert tick.index('m_Service.Tick();')<tick.index('m_ObservationFailure.IsEmpty()')<tick.index('if (!m_Flight.Finished())')
|
|
assert 'now - m_CruiseSince >= 10000' in tick and 'now - m_CruiseSince >= 1000' in tick
|
|
assert 'now - m_TranslationStable >= 5000' in tick and 'now - m_LandingStable >= 2000' in tick
|
|
stop=body(P,'void Stop(')
|
|
assert stop.index('s_Current = null')<stop.index('m_Service.Stop()')<stop.index('ERR_Actors.DeleteSpawned(')<stop.index('m_Service.Tick()')<stop.index('m_Flight = null')
|
|
assert 'CanDispose(' in stop
|
|
# Execute root-owned Chat dispatch branch: failure cannot become completion/reset.
|
|
chat=ROOT/'addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c';cs=chat.read_text()
|
|
a=cs.index('if (m_ERRSmokeStep == 240)');opening=cs.index('{',a);i=opening+1;depth=1
|
|
while depth:depth+=(cs[i]=='{')-(cs[i]=='}');i+=1
|
|
branch=cs[opening+1:i-1].replace('m_ERRAircraftField.Tick','m_ERRAircraftField.Tick').replace('ERR_Phase.DEFEAT','2')
|
|
run(r'''#include <cassert>
|
|
#include <initializer_list>
|
|
struct Fixture {int result=0;int Tick(float){return result;}};
|
|
struct Chat {Fixture m_ERRAircraftField;int m_ERRSmokeStep=240,failures=0,transitions=0;void ERR_Check(bool,const char*){failures++;}void Transition(int){transitions++;}void Tick(float now){
|
|
'''+branch+r'''} };
|
|
int main(){for(int result:{-1,0,1}){Chat c;c.m_ERRAircraftField.result=result;c.Tick(1000);assert(c.failures==(result<0));assert(c.transitions==(result==1));assert(c.m_ERRSmokeStep==(result==1?241:240));}}
|
|
''')
|
|
assert 'm_ERRTestAircraftField = outcome == "aircraft-field"' in cs
|
|
assert 'm_ERRTestAircraftControl || m_ERRTestAircraftRoute || m_ERRTestAircraftField' in cs and '+ 330000' in cs
|
|
assert 'm_ERRAircraftField.Stop()' in body(chat,'protected void ERR_StopHQPatrol(')
|
|
assert 'errSmokeFieldServiceCancel' in cs and 'current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 241' in cs
|
|
print('PASS: actual field result/braking observer and Chat dispatch rules; one finite fixed candidate, deferred callback failure and guarded cleanup; no native measurement')
|