Improve small-terrain placement and show startup failure causes; record Workshop 0.0.120
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
+41
-12
@@ -1,7 +1,7 @@
|
||||
"""Run production generic placement scheduling with clock/terrain adapters."""
|
||||
from policy_harness import ROOT, body, run
|
||||
base = ROOT/'addons/core/Scripts/Game/ERR/00_Core'
|
||||
s = '\n'.join(body(base/'ERR_GeneratedSites.c', signature) for signature in ('static bool StartupSite(', 'void StartupOnly(', 'bool StartupReady(', 'protected bool SkipOptional(')) + '\n' + body(base/'ERR_TerrainSites.c', 'override ERR_PrepareResult Tick(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool PrepareAirports(')
|
||||
s = '\n'.join(body(base/'ERR_GeneratedSites.c', signature) for signature in ('static bool StartupSite(', 'void StartupOnly(', 'bool StartupReady(', 'protected bool SkipOptional(')) + '\n' + body(base/'ERR_TerrainSites.c', 'override ERR_PrepareResult Tick(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool PrepareAirports(') + '\n' + '\n'.join(body(base/'ERR_TerrainSites.c', sig) for sig in ('static int SearchAttempts(', 'static int SearchMilliseconds(', 'protected void ExpandExtractionSearch('))
|
||||
for before, after in [('override ', ''), ('protected ', ''), ('ref ERR_SiteDefinition retained','Site* retained'),
|
||||
('ERR_SiteDefinition site','Site* site'), ('foreach (', 'for ('), ('site.', 'site->'), ('retained.','retained->'),
|
||||
('m_Definition.', 'm_Definition->'), ('m_Airports.', 'm_Airports->'), ('System.GetTickCount','Clock::Now'),
|
||||
@@ -27,6 +27,7 @@ template<class T>struct Array:std::vector<T>{using std::vector<T>::operator=;boo
|
||||
struct Definition{bool TerrainGenerated=true;Array<Site*> Sites;Site* required=nullptr;bool valid=true;bool Valid(){return valid&&(!required||Sites.Find(required)>=0);}};
|
||||
struct Airports{Array<vector> m_Positions;Airports(){m_Positions.push_back(Vector(0,0,0));}bool complete=true;int updates=0,cost=0,finishAfter=0;void Update(){updates++;Clock::now+=cost;if(finishAfter&&updates>=finishAfter)complete=true;}bool Complete(){return complete;}};
|
||||
struct Scheduler{
|
||||
void RecordRejection(string){}void ReportSearch(Site*,string){}void ReportProgress(Site*){}void ClearDiagnostics(){}string RejectionSummary(){return "terrain";}
|
||||
Airports airports;Airports* m_Airports=&airports;bool m_AirportsReady=true;
|
||||
Definition definition;Definition* m_Definition=&definition;Array<Site*> m_Pending;
|
||||
ERR_PrepareResult m_Result=Result::PENDING;string m_Error;
|
||||
@@ -74,16 +75,30 @@ int main(){
|
||||
// A streaming timeout releases the candidate, but never accepts unloaded geometry.
|
||||
{Clock::now=0;Scheduler s;Site a;s.m_Pending={&a};s.waiting=100;
|
||||
assert(s.Tick()==Result::PENDING&&s.m_Attempts==1);Clock::now=5001;s.Tick();assert(!a.PlacementResolved&&s.m_Attempts>=2);}
|
||||
// The former ceiling no longer ends a difficult prison search.
|
||||
{Clock::now=30001;Scheduler s;Site a;s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=12000;
|
||||
while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved);}
|
||||
// A recognized but unusable airport broadens once, preserving the total clock
|
||||
// and attempt counter and discarding any pending airport/navmesh candidate.
|
||||
for(int byTime:{0,1}){
|
||||
Clock::now=byTime?30001:1;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;
|
||||
s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=byTime?2:12000;s.m_HaveCandidate=true;
|
||||
int attempts=s.m_Attempts;s.ExpandExtractionSearch(&a);
|
||||
assert(!a.RequireAirfield&&!s.m_HaveCandidate&&s.m_Began==0&&s.m_Attempts==attempts&&a.Label=="Extraction landing zone");
|
||||
s.m_HaveCandidate=true;s.ExpandExtractionSearch(&a);assert(s.m_HaveCandidate); // one transition only
|
||||
while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&a.PlacementResolved);}
|
||||
{Clock::now=29990;Scheduler s;Site a;a.Kind=Kind::EXTRACTION;s.m_Attempts=11999;
|
||||
s.ExpandExtractionSearch(&a);assert(a.RequireAirfield);}
|
||||
// An exhausted budget rejects every mandatory kind WITHOUT trying the authored fallback.
|
||||
for(int kind:{Kind::PRISON,Kind::RADIO,Kind::EXTRACTION,Kind::MORTAR}){
|
||||
Clock::now=0;Scheduler s;Site a;a.Kind=kind;s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=12000;
|
||||
Clock::now=0;Scheduler s;Site a;a.Kind=kind;s.m_Pending={&a};s.definition.Sites={&a};s.m_Attempts=48000;
|
||||
assert(s.Tick()==Result::FAILED&&!a.PlacementResolved&&s.surveys==0&&a.Position[0]==11&&!s.m_Error.empty());}
|
||||
{Clock::now=30001;Scheduler s;Site a;s.m_Pending={&a};assert(s.Tick()==Result::FAILED&&s.surveys==0);}
|
||||
{Clock::now=120001;Scheduler s;Site a;s.m_Pending={&a};assert(s.Tick()==Result::FAILED&&s.surveys==0);}
|
||||
// Optional sites can be removed only while preserving minimum objective/intel validity.
|
||||
for(int kind:{Kind::OUTPOST,Kind::HOSPITAL,Kind::DEPOT}){
|
||||
Clock::now=0;Scheduler s;Site a,b;a.Kind=kind;s.m_Pending={&a,&b};s.definition.Sites={&a,&b};s.m_Attempts=12000;
|
||||
Clock::now=0;Scheduler s;Site a,b;a.Kind=kind;s.m_Pending={&a,&b};s.definition.Sites={&a,&b};s.m_Attempts=48000;
|
||||
while(s.Tick()==Result::PENDING){}assert(s.m_Result==Result::READY&&!a.PlacementResolved&&b.PlacementResolved&&s.definition.Sites.Count()==1);}
|
||||
{Clock::now=0;Scheduler s;Site a;a.Kind=Kind::DEPOT;s.m_Pending={&a};s.definition.Sites={&a};s.definition.required=&a;s.m_Attempts=12000;
|
||||
{Clock::now=0;Scheduler s;Site a;a.Kind=Kind::DEPOT;s.m_Pending={&a};s.definition.Sites={&a};s.definition.required=&a;s.m_Attempts=48000;
|
||||
assert(s.Tick()==Result::FAILED&&s.definition.Sites.Count()==1);}
|
||||
{Clock::now=0;Scheduler s;s.definition.valid=false;assert(s.Tick()==Result::FAILED);}
|
||||
}
|
||||
@@ -94,12 +109,13 @@ nav = body(base/'ERR_TerrainSites.c', 'protected bool Navigable(')
|
||||
nav = nav.replace('protected ', '').replace('NavmeshWorldComponent mesh', 'Mesh* mesh').replace('mesh.', 'mesh->')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
struct vector{};
|
||||
struct Mesh{bool loaded=true,loadable=false,reachable=false;int requests=0,queries=0;bool IsTileLoaded(vector){return loaded;}bool LoadTileIn(vector){requests++;return loadable;}bool GetReachablePoint(vector,int,vector&){queries++;return reachable;}} mesh;
|
||||
struct World{bool available=true;Mesh* GetNavmeshWorldComponent(const char*){return available?&mesh:nullptr;}} world;
|
||||
struct Game{World& GetAIWorld(){return world;}} game;
|
||||
Game& GetGame(){return game;}
|
||||
struct Survey{bool m_WaitingNav=false;
|
||||
struct Survey{std::string m_NavigationReason;bool m_WaitingNav=false;
|
||||
'''+nav+r'''
|
||||
};
|
||||
int main(){
|
||||
@@ -115,7 +131,7 @@ print('PASS: actual navigation readiness supports static meshes, yields only for
|
||||
|
||||
# Execute sampling and clearance selection: an empty airport list must never be
|
||||
# indexed, and generic helicopter pads retain geometry/nav/physical validation.
|
||||
placement = body(base/'ERR_TerrainSites.c', 'protected vector SamplePosition(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool Survey(')
|
||||
placement = body(base/'ERR_TerrainSites.c', 'static bool Bounds(') + '\n' + body(base/'ERR_TerrainSites.c', 'static float PrisonAirportBuffer(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected vector SamplePosition(') + '\n' + body(base/'ERR_TerrainSites.c', 'protected bool Survey(')
|
||||
for before, after in [
|
||||
('protected ', ''), ('ERR_SiteDefinition site', 'Site* site'), ('site.', 'site->'),
|
||||
('out float floor', 'float& floor'), ('m_Airports.', 'm_Airports->'),
|
||||
@@ -126,6 +142,7 @@ for before, after in [
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
struct vector {float a[3];float& operator[](int i){return a[i];}};
|
||||
vector Vector(float x,float y,float z){return {{x,y,z}};}
|
||||
@@ -136,6 +153,7 @@ namespace Math {
|
||||
int indexed=0;const float PI2=6.283185;
|
||||
float RandomFloat(float a,float b){return (a+b)/2;}
|
||||
int RandomInt(int a,int b){assert(b>a);indexed++;return a;}
|
||||
float Min(float a,float b){return a<b?a:b;}float Clamp(float v,float lo,float hi){return v<lo?lo:(v>hi?hi:v);}
|
||||
float Cos(float a){return std::cos(a);}float Sin(float a){return std::sin(a);}
|
||||
}
|
||||
namespace ERR_Actors {vector Ground(vector p){return p;}}
|
||||
@@ -149,11 +167,13 @@ namespace ERR_RescueLanding {bool fits=true;int checks=0;bool Fits(vector){check
|
||||
template<class T>struct Array:std::vector<T>{bool IsEmpty(){return this->empty();}int Count(){return this->size();}};
|
||||
struct Airports {Array<vector> m_Positions;};
|
||||
struct Terrain {
|
||||
bool geometry=true,flat=true;
|
||||
bool Site(::Site*,vector,float,float& floor){floor=42;return geometry;}
|
||||
std::string Rejection(){return "terrain";}
|
||||
bool geometry=true,flat=true;float airportBuffer=0;
|
||||
bool Site(::Site*,vector,float,float& floor,float buffer){airportBuffer=buffer;floor=42;return geometry;}
|
||||
bool Clear(vector,float,float,float,float&){return flat;}
|
||||
};
|
||||
struct Placement {
|
||||
std::string m_NavigationReason="navigation";void RecordRejection(std::string){}
|
||||
Airports airports;Airports* m_Airports=&airports;Terrain m_Terrain;
|
||||
vector m_Low=Vector(1000,0,-5000),m_High=Vector(9000,0,3000);
|
||||
int m_GeometryRejected=0,m_NavigationRejected=0,m_PadRejected=0;
|
||||
@@ -162,12 +182,21 @@ struct Placement {
|
||||
};
|
||||
int main(){
|
||||
Placement s;Site site;float floor=0;
|
||||
assert(s.Bounds(Vector(-500,0,-500),Vector(500,0,500)));
|
||||
assert(!s.Bounds(Vector(0,0,0),Vector(999,0,2000)));
|
||||
assert(!s.Bounds(Vector(0,0,0),Vector(2000,0,999)));
|
||||
assert(!s.Bounds(Vector(0,0,0),Vector(100001,0,2000)));
|
||||
assert(s.PrisonAirportBuffer(Vector(0,0,0),Vector(2000,0,2000))==300);
|
||||
assert(s.PrisonAirportBuffer(Vector(-100,0,-100),Vector(900,0,900))==200);
|
||||
assert(s.PrisonAirportBuffer(Vector(0,0,0),Vector(20000,0,20000))==1500);
|
||||
assert(s.PrisonAirportBuffer(Vector(0,0,0),Vector(2000,0,20000))==300);
|
||||
auto p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==0);
|
||||
s.airports.m_Positions.push_back(Vector(2000,0,3000));
|
||||
s.airports.m_Positions.push_back(Vector(2000,0,3000));site.RequireAirfield=true;
|
||||
p=s.SamplePosition(&site);assert(Math::indexed==1&&std::abs(p[0]-1775)<.01&&std::abs(p[2]-3000)<.01);
|
||||
site.Kind=Kind::PRISON;p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==1);
|
||||
site.Kind=Kind::EXTRACTION;
|
||||
assert(s.Survey(&site,p,0,floor)&&floor==42);assert(ERR_AirfieldPlacement::groundChecks==1&&ERR_AirfieldPlacement::airportChecks==0);
|
||||
site.Kind=Kind::EXTRACTION;site.RequireAirfield=false;
|
||||
p=s.SamplePosition(&site);assert(p[0]==5000&&p[2]==-1000&&Math::indexed==1);
|
||||
assert(s.Survey(&site,p,0,floor)&&floor==42);assert(ERR_AirfieldPlacement::groundChecks==1&&ERR_AirfieldPlacement::airportChecks==0&&s.m_Terrain.airportBuffer==1200);
|
||||
ERR_AirfieldPlacement::ground=false;assert(!s.Survey(&site,p,0,floor)&&s.m_PadRejected==1);
|
||||
site.RequireAirfield=true;ERR_AirfieldPlacement::ground=true;
|
||||
assert(!s.Survey(&site,p,0,floor));assert(ERR_AirfieldPlacement::airportChecks==1&&ERR_AirfieldPlacement::groundChecks==2);
|
||||
|
||||
Reference in New Issue
Block a user