Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
179 lines
14 KiB
Python
179 lines
14 KiB
Python
"""Execute bounded whole-field survey with fake native terrain/traces, never fly."""
|
|
from policy_harness import ROOT, body, run
|
|
import re
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFieldEnvelope.c'
|
|
RULES=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_AircraftFieldRules.c'
|
|
def cpp(s):
|
|
for name in ('Math','vector','ERR_AircraftFieldRules','SCR_TerrainHelper','TraceFlags'):
|
|
s=s.replace(name+'.',name+'::')
|
|
s=s.replace('array<IEntity> excluded = null','void* excluded = nullptr')
|
|
s=s.replace('TraceBox corridor','TraceBox* corridor').replace('TraceBox low','TraceBox* low')
|
|
for name in ('corridor','low'):s=s.replace(name+'.',name+'->')
|
|
s=s.replace('GetGame().','GetGame()->').replace('GetWorld().','GetWorld()->').replace(', null)', ', nullptr)')
|
|
return re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])', r'\1f', s)
|
|
survey_code = (r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <cstdio>
|
|
struct Math {static float Clamp(float v,float lo,float hi){return std::clamp(v,lo,hi);}static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}static int Ceil(float x){return std::ceil(x);}static float AbsFloat(float x){return std::abs(x);}};
|
|
struct vector {float v[3]={};vector(){}vector(float x,float y,float z){v[0]=x;v[1]=y;v[2]=z;}vector(const char*s){sscanf(s,"%f %f %f",v,v+1,v+2);}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 n)const{return {v[0]*n,v[1]*n,v[2]*n};}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]);}};
|
|
vector Vector(float x,float y,float z){return {x,y,z};}
|
|
struct ERR_AircraftFieldRules {
|
|
'''+cpp(body(RULES,'static bool Length('))+r'''
|
|
};
|
|
namespace TraceFlags {enum {ENTS=1,WORLD=2};}
|
|
struct TraceBox {vector Start,End,Mins,Maxs;int Flags=0;void* ExcludeArray=nullptr;};
|
|
struct World {int calls=0,refuseAt=0;std::vector<TraceBox> seen;float TraceMove(TraceBox* t,void*){calls++;seen.push_back(*t);delete t;return calls==refuseAt?.5:1;}} world;
|
|
struct Game {bool hasWorld=true;World* GetWorld(){return hasWorld?&world:nullptr;}} game;
|
|
bool hasGame=true;Game* GetGame(){return hasGame?&game:nullptr;}
|
|
struct SCR_TerrainHelper {
|
|
static int samples,kind;static std::vector<vector> points;
|
|
static float GetTerrainY(vector p){samples++;points.push_back(p);if(kind==1)return 10.31f;if(kind==2&&samples==2000)return 9.69f;if(kind==3)return NAN;return 10;}
|
|
};
|
|
int SCR_TerrainHelper::samples=0;int SCR_TerrainHelper::kind=0;std::vector<vector> SCR_TerrainHelper::points;
|
|
static const int MAX_TERRAIN_NODES=4096;
|
|
'''+ '\n'.join(cpp(body(P,key)) for key in ('static bool FinitePoint(', 'static int TerrainColumns(', 'static bool TerrainBudget(', 'static bool Survey('))+r'''
|
|
void reset(){world=World();SCR_TerrainHelper::samples=0;SCR_TerrainHelper::kind=0;SCR_TerrainHelper::points.clear();hasGame=true;game.hasWorld=true;}
|
|
int main(){
|
|
assert(TerrainColumns(100)==90);assert(TerrainColumns(100.05f)==91);
|
|
assert(TerrainColumns(99.94f)==0&&TerrainColumns(100.06f)==0&&TerrainColumns(NAN)==0);
|
|
assert(TerrainBudget(90,40)&&TerrainBudget(91,40)&&TerrainBudget(63,63));
|
|
assert(!TerrainBudget(64,63)&&!TerrainBudget(0,40)&&!TerrainBudget(4096,1)&&!TerrainBudget(-1,40));
|
|
assert(!FinitePoint({NAN,10,0})&&!FinitePoint({0,INFINITY,0})&&!FinitePoint({0,10,1000001}));
|
|
for(float angle: {0.f,.4f,1.2f,2.8f}){
|
|
reset();vector a={100,10,200},u={std::cos(angle),0,std::sin(angle)},v={-u[2],0,u[0]},b=a+u*100;
|
|
int excluded=7;assert(Survey(a,b,&excluded));
|
|
int columns=TerrainColumns(vector::DistanceXZ(a,b));
|
|
assert(SCR_TerrainHelper::samples==(columns+1)*41&&SCR_TerrainHelper::samples<=4096&&world.calls==2);
|
|
auto high=world.seen[0],low=world.seen[1];assert(high.Flags==(TraceFlags::ENTS|TraceFlags::WORLD)&&low.Flags==TraceFlags::ENTS);
|
|
assert(high.ExcludeArray==&excluded&&low.ExcludeArray==&excluded);
|
|
assert(high.Start[1]+high.Mins[1]==12&&high.End[1]+high.Maxs[1]==71);
|
|
assert(std::abs(low.Start[1]+low.Mins[1]-10.3f)<.0001);
|
|
// Assert the real terrain-query coordinates, including both endpoint caps.
|
|
float minimumAlong=1e6,maximumAlong=-1e6,minimumAcross=1e6,maximumAcross=-1e6;
|
|
for(auto p:SCR_TerrainHelper::points){float along=vector::Dot(p-a,u),across=vector::Dot(p-a,v);minimumAlong=std::min(minimumAlong,along);maximumAlong=std::max(maximumAlong,along);minimumAcross=std::min(minimumAcross,across);maximumAcross=std::max(maximumAcross,across);}
|
|
assert(std::abs(minimumAlong+40)<.001&&std::abs(maximumAlong-140)<.001&&std::abs(minimumAcross+40)<.001&&std::abs(maximumAcross-40)<.001);
|
|
for(size_t i=0;i<SCR_TerrainHelper::points.size();i++){
|
|
if(i%41)assert(vector::DistanceXZ(SCR_TerrainHelper::points[i],SCR_TerrainHelper::points[i-1])<=2.0001);
|
|
if(i>=41)assert(vector::DistanceXZ(SCR_TerrainHelper::points[i],SCR_TerrainHelper::points[i-41])<=2.0001);
|
|
}
|
|
// Every sampled bearing of the entire40m segment tube fits both traces.
|
|
for(int step=0;step<=10;step++)for(int bearing=0;bearing<16;bearing++){
|
|
float theta=bearing*6.2831853f/16;vector p=a+u*(step*10)+Vector(std::cos(theta)*40,0,std::sin(theta)*40);
|
|
for(int axis:{0,2}){assert(p[axis]>=high.Start[axis]+high.Mins[axis]-.001&&p[axis]<=high.Start[axis]+high.Maxs[axis]+.001);assert(low.Mins[axis]==high.Mins[axis]&&low.Maxs[axis]==high.Maxs[axis]);}
|
|
}
|
|
}
|
|
vector a={0,10,0},b={100,10,0};
|
|
for(int fault=1;fault<=3;fault++){reset();SCR_TerrainHelper::kind=fault;assert(!Survey(a,b));assert(world.calls==0&&SCR_TerrainHelper::samples<=4096);if(fault==2)assert(SCR_TerrainHelper::samples==2000);}
|
|
for(int trace=1;trace<=2;trace++){reset();world.refuseAt=trace;assert(!Survey(a,b));assert(world.calls==trace&&SCR_TerrainHelper::samples==3731);}
|
|
for(vector end:{Vector(99.94f,10,0),Vector(100.06f,10,0),Vector(100,10.31f,0),Vector(NAN,10,0)}){reset();assert(!Survey(a,end));assert(world.calls==0&&SCR_TerrainHelper::samples==0);}
|
|
reset();assert(!Survey({0,1,0},{100,1,0}));assert(SCR_TerrainHelper::samples==0);
|
|
reset();hasGame=false;assert(!Survey(a,b));assert(SCR_TerrainHelper::samples==0);
|
|
reset();game.hasWorld=false;assert(!Survey(a,b));assert(SCR_TerrainHelper::samples==0);
|
|
reset();assert(Survey(a,{100.05f,10,0}));assert(SCR_TerrainHelper::samples==3772);
|
|
}
|
|
''')
|
|
run(survey_code)
|
|
# Execute the same actual Survey with WORKBENCH enabled. Native logging and
|
|
# entity metadata are boundary doubles; every terrain/trace call remains real
|
|
# to this actual-method harness. Refusal must log once without querying again.
|
|
wb = '#define WORKBENCH\n' + survey_code
|
|
wb = wb.replace('void reset(){world=World();', 'void reset(){diagnosticCount=0;world=World();')
|
|
logging = r'''
|
|
static bool diagnosticEnabled=true;static int diagnosticCount=0;
|
|
static bool SurveyDiagnostics(){return diagnosticEnabled;}
|
|
template<typename...T>void PrintFormat(const char*,T...){diagnosticCount++;}
|
|
static void SurveyTraceFailure(const char*,float fraction,TraceBox*){assert(fraction==.5f);if(diagnosticEnabled)diagnosticCount++;}
|
|
'''
|
|
wb = wb.replace('static const int MAX_TERRAIN_NODES=4096;', logging+'\nstatic const int MAX_TERRAIN_NODES=4096;')
|
|
wb = wb.replace('assert(Survey(a,b,&excluded));', 'assert(Survey(a,b,&excluded));assert(diagnosticCount==0);')
|
|
wb = wb.replace('assert(!Survey(a,b));', 'assert(!Survey(a,b));assert(diagnosticCount==1);')
|
|
wb = wb.replace('assert(!Survey(a,end));', 'assert(!Survey(a,end));assert(diagnosticCount==1);')
|
|
wb = wb.replace('reset();assert(Survey(a,{100.05f,10,0}));', 'reset();diagnosticEnabled=false;SCR_TerrainHelper::kind=1;assert(!Survey(a,b)&&diagnosticCount==0&&SCR_TerrainHelper::samples==1);reset();diagnosticEnabled=true;assert(Survey(a,{100.05f,10,0}));')
|
|
run(wb)
|
|
source=P.read_text();qualify=body(P,'static ERR_AircraftFieldEnvelope QualifyField(')
|
|
assert 'bool Claim(' not in source and 'bool ClaimedBy(' not in source
|
|
assert 'ERR_AircraftFlightEnvelope.Qualify(mode, aircraft, pilot)' in qualify
|
|
assert qualify.index('ERR_AircraftFlightEnvelope.Qualify(')<qualify.index('Hierarchy(')<qualify.index('ClearColumn(')<qualify.index('Survey(')<qualify.index('new ERR_AircraftFieldEnvelope')
|
|
assert 'm_Home = point.Home()' in qualify and 'm_Radius = point.Radius()' in qualify
|
|
assert source.count('m_Destination = end')==1
|
|
assert 'record.m_QualifiedAt = began;' in qualify and 'now - began <= 2000' in qualify
|
|
assert 'vector.Distance(aircraft.GetOrigin(), point.Home()) > 0.1' in qualify
|
|
assert 'GetGame().GetWorld() != world' in qualify
|
|
assert 'ERR_AircraftFieldRules.Contains(m_Home, m_Destination, position, upright)' in body(P,'bool Contains(')
|
|
print('PASS: actual100m whole-field survey sampling/4096cap, rotated coverage, all-node terrain and native trace refusal; original certificate lineage retained; no native qualification')
|
|
|
|
# Execute the actual certificate issuance body with fake native boundaries.
|
|
# Geometry/terrain/trace internals are exercised above, not replaced in that test.
|
|
q=cpp(qualify)
|
|
q=q.replace('static ERR_AircraftFieldEnvelope QualifyField(', 'static Field* QualifyField(')
|
|
q=q.replace('ERR_GameMode mode','Mode* mode').replace('Vehicle aircraft','Entity* aircraft').replace('IEntity pilot','Entity* pilot')
|
|
q=q.replace('BaseWorld world','World* world').replace('ERR_AircraftFlightEnvelope point','Base* point')
|
|
q=q.replace('ERR_AircraftFlightEnvelope.Qualify','Base::Qualify').replace('ERR_Actors.Ground','Ground')
|
|
q=q.replace('array<IEntity> excluded = {}','int excluded = 1')
|
|
q=q.replace('ERR_AircraftFieldEnvelope record = new ERR_AircraftFieldEnvelope()','Field* record = new Field()')
|
|
q=q.replace('ERR_GameMode.Get()','Mode::Get()')
|
|
for name in ('world','point','record','mode','aircraft','pilot'):q=q.replace(name+'.',name+'->')
|
|
q=q.replace('return null;', 'return nullptr;')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
struct vector{float v[3]={};vector(){}vector(float x,float y,float z){v[0]=x;v[1]=y;v[2]=z;}float&operator[](int i){return v[i];}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]);}static float Distance(vector a,vector b){return std::sqrt((a[0]-b[0])*(a[0]-b[0])+(a[1]-b[1])*(a[1]-b[1])+(a[2]-b[2])*(a[2]-b[2]));}};
|
|
struct Math{static float AbsFloat(float x){return std::abs(x);}};
|
|
struct ERR_AircraftFieldRules{
|
|
'''+cpp(body(RULES,'static bool Length('))+r'''
|
|
};
|
|
struct World{float now=1000;float GetWorldTime(){return now;}} world,foreignWorld;
|
|
struct Game{World* current=&world;World* GetWorld(){return current;}} game;
|
|
Game* liveGame=&game;Game* GetGame(){return liveGame;}
|
|
struct Mode{bool active=true;bool Active(){return active;}static Mode* current;static Mode* Get(){return current;}} mode,foreignMode;Mode* Mode::current=&mode;
|
|
struct Entity{World* worldPtr=&world;vector origin={0,10.1f,0};World* GetWorld(){return worldPtr;}vector GetOrigin(){return origin;}} aircraft,pilot;
|
|
int fault=0,allocations=0;std::vector<int> order;
|
|
vector Ground(vector point){point[1]=10;return point;}
|
|
struct Base {
|
|
Mode* m_Mode=nullptr;Entity* m_Aircraft=nullptr;Entity* m_Pilot=nullptr;vector m_Home={0,10.1f,0};float m_Radius=28,m_QualifiedAt=-1;
|
|
vector Home(){return m_Home;}float Radius(){return m_Radius;}
|
|
static Base* Qualify(Mode*,Entity*,Entity*){order.push_back(1);static Base point;return fault==1?nullptr:&point;}
|
|
};
|
|
struct Field:Base{
|
|
vector m_Destination;Field(){allocations++;}
|
|
'''+cpp(body(P,'static bool FinitePoint('))+r'''
|
|
static bool Hierarchy(Entity* entity,int){order.push_back(entity==&aircraft?2:3);return fault!=(entity==&aircraft?2:3);}
|
|
static bool ClearColumn(Entity*,Entity*,vector){order.push_back(4);return fault!=4;}
|
|
static bool Survey(vector,vector,int){order.push_back(5);world.now+=500;if(fault==6)world.now=3001;if(fault==7)aircraft.origin[0]=.101f;if(fault==8)game.current=&foreignWorld;if(fault==9)Mode::current=&foreignMode;if(fault==10)mode.active=false;if(fault==11)pilot.worldPtr=&foreignWorld;return fault!=5;}
|
|
'''+q+r'''
|
|
};
|
|
void reset(){fault=0;world.now=1000;game.current=&world;Mode::current=&mode;mode.active=true;aircraft.origin={0,10.1f,0};aircraft.worldPtr=&world;pilot.worldPtr=&world;allocations=0;order.clear();}
|
|
int main(){
|
|
reset();Field* qualified=Field::QualifyField(&mode,&aircraft,&pilot,{100,999,0});
|
|
assert(qualified&&allocations==1&&order==std::vector<int>({1,2,3,4,5}));
|
|
assert(qualified->m_Mode==&mode&&qualified->m_Aircraft==&aircraft&&qualified->m_Pilot==&pilot);
|
|
assert(qualified->m_QualifiedAt==1000&&world.now==1500); // survey never renews age
|
|
assert(qualified->m_Radius==28&&qualified->m_Home[1]==10.1f);
|
|
assert(qualified->m_Destination[0]==100&&qualified->m_Destination[1]==10.1f);
|
|
delete qualified;
|
|
for(int problem=1;problem<=11;problem++){reset();fault=problem;assert(!Field::QualifyField(&mode,&aircraft,&pilot,{100,10,0}));assert(allocations==0);}
|
|
reset();assert(!Field::QualifyField(&mode,&aircraft,&pilot,{99,10,0}));assert(order==std::vector<int>({1})&&allocations==0);
|
|
reset();assert(!Field::QualifyField(&mode,&aircraft,&pilot,{NAN,10,0}));assert(order.empty()&&allocations==0);
|
|
}
|
|
''')
|
|
print('PASS: actual QualifyField preserves original identity/footprint/time, orders complete surveys before issuance, and refuses failed stages/moved body/expired or changed world; native boundaries faked')
|
|
|
|
diag=body(P,'protected static void SurveyTraceFailure(')
|
|
assert 'trace.TraceEnt' in diag and 'hit.GetPrefabData().GetPrefabName()' in diag
|
|
for mutation in ('TraceMove(', 'Set', 'Spawn(', 'Delete(', 'GetTerrainY('):assert mutation not in diag
|
|
# Exact optional scope executes separately; absent/wrong flags never log.
|
|
scope=body(P,'protected static bool SurveyDiagnostics(').replace('protected ','').replace('System.','System::').replace('string enabled','std::string enabled')
|
|
run(r'''#include <cassert>
|
|
#include <string>
|
|
struct System {static std::string smoke,outcome;static bool GetCLIParam(std::string key,std::string& out){out=key=="errSmoke"?smoke:outcome;return !out.empty();}};
|
|
std::string System::smoke="",System::outcome="";
|
|
'''+scope+r'''
|
|
int main(){assert(!SurveyDiagnostics());System::smoke="1";assert(!SurveyDiagnostics());System::outcome="aircraft-route";assert(!SurveyDiagnostics());System::outcome="aircraft-field";assert(SurveyDiagnostics());System::smoke="0";assert(!SurveyDiagnostics());}
|
|
''')
|
|
print('PASS: WORKBENCH first-refusal diagnostics preserve ordinary predicates, query order/count/budget and opt-in scope; no native logging run')
|