Files

99 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Execute the shipped checkpoint layout across road widths and rotations."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_Roadblocks.c'
s='\n'.join(body(p,x) for x in ('protected void Layout(', 'protected void AddProp('))
s=s.replace('protected ', '').replace('out ', '').replace('array<ref ERR_RoadblockProp> covers', 'Array<Prop*>& covers')
s=s.replace('ResourceName ', 'string ').replace('vector ', 'Vec ').replace('ERR_Actors.Ground','Ground').replace('new ERR_RoadblockProp','new Prop')
run(r'''
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
using string=std::string;
struct Vec{double x=0,y=0,z=0;Vec operator+(Vec b)const{return{x+b.x,y+b.y,z+b.z};}Vec operator-(Vec b)const{return{x-b.x,y-b.y,z-b.z};}Vec operator*(double k)const{return{x*k,y*k,z*k};}};
double dot(Vec a,Vec b){return a.x*b.x+a.y*b.y+a.z*b.z;}
Vec Ground(Vec p){p.y=50;return p;}
template<class T>struct Array:std::vector<T>{void Clear(){for(auto*p:*this)delete p;this->clear();}void Insert(T p){this->push_back(p);}};
struct Prop{string Prefab;Vec Position,Forward,Right;double HalfWidth,HalfDepth,Height;
Prop(string p,Vec v,Vec f,double w,double d,double h):Prefab(p),Position(v),Forward(f),Right({f.z,0,-f.x}),HalfWidth(w),HalfDepth(d),Height(h){}
};
struct Checkpoint{Vec m_Centre={1000,50,2000},m_Forward,m_Right;double m_Shoulder;
string COVER="cover",BARRIER="barrier",OBSTACLE="obstacle",SIGN="sign",TABLE="table",SHELTER="shelter";
'''+s+r'''
};
int main(){
for(double width:{4.,6.,10.,16.})for(int degrees=0;degrees<360;degrees+=15){
Checkpoint l;double angle=degrees*3.141592653589793/180;
l.m_Forward={std::sin(angle),0,std::cos(angle)};l.m_Right={std::cos(angle),0,-std::sin(angle)};
l.m_Shoulder=std::max(width*.5+1.5,4.);Array<Prop*>props;l.Layout(props);assert(props.size()==18);
std::map<string,int>counts;std::vector<double>barriers;
for(auto*p:props){counts[p->Prefab]++;
Vec delta=p->Position-l.m_Centre;double across=dot(delta,l.m_Right),along=dot(delta,l.m_Forward);
// Whole footprints (not just origins) preserve a four-metre straight passage.
double reach=std::abs(dot(p->Right,l.m_Right))*p->HalfWidth+std::abs(dot(p->Forward,l.m_Right))*p->HalfDepth;
assert(std::abs(across)-reach>=2.-1e-8);
if(p->Prefab=="barrier")barriers.push_back(along);
if(p->Prefab=="sign")assert(dot(p->Forward,l.m_Forward)*along>0); // both approaches face outward
// Guard starting volumes cannot intersect any prop in the planned post.
for(int side:{-1,1})for(int row:{-1,1}){
Vec guard=l.m_Centre+l.m_Right*(side*(l.m_Shoulder+2.5))+l.m_Forward*(side*(row-1.5));
Vec rel=guard-p->Position;
assert(std::abs(dot(rel,p->Right))>p->HalfWidth+.6 || std::abs(dot(rel,p->Forward))>p->HalfDepth+.6);
}
}
assert(counts["cover"]==8&&counts["barrier"]==4&&counts["obstacle"]==2&&counts["sign"]==2&&counts["table"]==1&&counts["shelter"]==1);
assert(barriers.size()==4&&std::abs(barriers[0]-barriers[2])>15.9);
// No physical prop footprint intersects another (separating axes).
for(size_t i=0;i<props.size();i++)for(size_t j=i+1;j<props.size();j++){
auto*a=props[i];auto*b=props[j];bool separate=false;
for(Vec axis:{a->Right,a->Forward,b->Right,b->Forward}){
double ar=std::abs(dot(a->Right,axis))*a->HalfWidth+std::abs(dot(a->Forward,axis))*a->HalfDepth;
double br=std::abs(dot(b->Right,axis))*b->HalfWidth+std::abs(dot(b->Forward,axis))*b->HalfDepth;
if(std::abs(dot(a->Position-b->Position,axis))>=ar+br)separate=true;
}
assert(separate);
}
props.Clear();
}
}
''')
print('PASS: shipped checkpoint layout preserves vehicle lane, opposing signs, staggered barriers, clear guard slots and separate props across 4–16m roads and all tested headings')
# Execute the actual clearance path: owned pieces are excluded only after
# checking guards; terrain and foreign obstructions remain authoritative.
s=body(p,'protected bool CoverClear(').replace('protected ', '').replace('ERR_RoadblockProp prop','Prop prop')
s=s.replace('vector ', 'Vec ').replace('foreach (IEntity builtEntity : m_BuildSpawned)', 'for (Node* builtEntity : m_BuildSpawned)')
s=s.replace('ChimeraCharacter.Cast(builtEntity)', 'builtEntity->character').replace('builtEntity.GetOrigin()', 'builtEntity->point')
s=s.replace('ERR_Actors.Ground','Ground').replace('Vector(', 'Vec(').replace('Math.AbsFloat', 'std::abs').replace('vector.Dot','dot')
s=s.replace('ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), point)', 'water').replace('GetGame().GetWorld().TraceMove(trace, null)', 'TraceMove(trace)')
s=s.replace('TraceBox trace = new TraceBox();','TraceBox trace;').replace('"0 0.15 0"','Vec(0,.15,0)').replace('"0 0.01 0"','Vec(0,.01,0)').replace('TraceFlags.', 'TraceFlags::')
run(r'''
#include <cassert>
#include <cmath>
#include <vector>
struct Vec {double v[3];Vec(double x=0,double y=0,double z=0):v{x,y,z}{}double& operator[](int i){return v[i];}Vec operator+(Vec b){return {v[0]+b[0],v[1]+b[1],v[2]+b[2]};}Vec operator-(Vec b){return {v[0]-b[0],v[1]-b[1],v[2]-b[2]};}Vec operator*(double n){return {v[0]*n,v[1]*n,v[2]*n};}};
double dot(Vec a,Vec b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];}
struct Node{bool character;Vec point;};
struct Prop{Vec Position,Right={1,0,0},Forward={0,0,1};double HalfWidth=1.9,HalfDepth=.7,Height=1;};
namespace TraceFlags{int WORLD=1,ENTS=2;}
struct TraceBox{Vec Start,End,Mins,Maxs;int Flags=0;std::vector<Node*>ExcludeArray;};
struct Clearance{
bool water=false,blocked=false;double slope=0;int traces=0;std::vector<Node*>m_BuildSpawned;
Vec Ground(Vec p){p[1]=slope*p[0];return p;}
double TraceMove(TraceBox trace){traces++;assert(trace.Flags==3&&trace.ExcludeArray==m_BuildSpawned);assert(std::abs(trace.Mins[0]+1.9)<1e-5&&std::abs(trace.Maxs[2]-.7)<1e-5);return blocked?0:1;}
'''+s+r'''
};
int main(){
Prop prop;Clearance c;assert(c.CoverClear(prop));
c.water=true;assert(!c.CoverClear(prop));c.water=false;
c.slope=.2;assert(!c.CoverClear(prop));c.slope=0;
c.blocked=true;assert(!c.CoverClear(prop));c.blocked=false;
Node ownProp{false,{0,0,0}},guard{true,{3,0,0}};c.m_BuildSpawned={&ownProp,&guard};assert(c.CoverClear(prop));
int traces=c.traces;guard.point={2,0,0};assert(!c.CoverClear(prop)&&c.traces==traces);
guard.point={0,0,1.4};assert(c.CoverClear(prop));guard.point={0,0,1};assert(!c.CoverClear(prop));
}
''')
print('PASS: actual prop clearance excludes owned geometry while retaining moved-guard, foreign-obstruction, water and terrain rejection')