Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
115 lines
7.6 KiB
Python
115 lines
7.6 KiB
Python
"""Execute delivered physical-patch coverage, without treating entire cells as searched."""
|
|
from policy_harness import ROOT, body, run
|
|
import re
|
|
P=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQCoverage.c'
|
|
section=P.read_text().split('class ERR_HQLocalCoverage\n')[1]
|
|
def method(signature):
|
|
start=section.index(signature); opening=section.index('{',start); end=opening+1; depth=1
|
|
while depth:
|
|
depth+=(section[end]=='{')-(section[end]=='}');end+=1
|
|
return section[start:end]
|
|
def cpp(s):
|
|
s=s.replace('Math.','Math::').replace('vector.','vector::')
|
|
for cls in ('ERR_HQCoverageRules','ERR_HQLocalCoverageRules','ERR_HQPursuitRules'):
|
|
s=s.replace(cls+'.',cls+'::')
|
|
for cls in ('ERR_HQSearchReport','ERR_HQSearchPatch'):
|
|
s=re.sub(r'\b'+cls+r' (\w+)',cls+r'* \1',s)
|
|
for name in ('report','patch','positive'):
|
|
s=s.replace(name+'.',name+'->')
|
|
s=re.sub(r'(m_(?:Patches|Positives)\[[^]]+\])\.',r'\1->',s)
|
|
s=s.replace('foreach (ERR_HQSearchPatch* positive : m_Positives)','for (ERR_HQSearchPatch* positive : m_Positives)')
|
|
s=s.replace('foreach (ERR_HQSearchPatch* patch : m_Patches)','for (ERR_HQSearchPatch* patch : m_Patches)')
|
|
s=s.replace('array<vector> reserved','Array<vector>* reserved').replace('out vector ','vector& ')
|
|
s=s.replace('reserved.Count()','reserved->Count()').replace('foreach (vector other : reserved)','for (vector other : *reserved)')
|
|
s=s.replace('= null','= nullptr').replace('bool occupied;','bool occupied=false;').replace('bool found;','bool found=false;')
|
|
s=s.replace('int oldest;','int oldest=0;').replace('float strongest;','float strongest=0;')
|
|
return s
|
|
rules='\n'.join(cpp(body(P,s)) for s in ('static bool RadiusValid(', 'static float Fraction('))
|
|
methods='\n'.join(cpp(method(s)) for s in ('int Count(', 'void Forget(', 'bool Ingest(', 'float Multiplier(', 'bool Select(', 'void Clear('))
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <vector>
|
|
struct vector{float x=0,y=0,z=0;float&operator[](int i){return i==0?x:i==1?y:z;}static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
|
|
struct Math{static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}};
|
|
template<class T>struct Array:std::vector<T>{int Count(){return this->size();}void Insert(T p){this->push_back(p);}void Remove(int i){this->erase(this->begin()+i);}void Clear(){this->clear();}};
|
|
template<class K,class V>struct Map:std::map<K,V>{int Count(){return this->size();}bool Contains(K k){return this->count(k);}V Get(K k){return this->at(k);}void Set(K k,V v){(*this)[k]=v;}void Clear(){this->clear();}};
|
|
struct ERR_HQPursuitRules{static bool Finite(float v){return std::isfinite(v);}};
|
|
struct ERR_HQCoverageRules{
|
|
static bool PositionValid(vector p){return std::isfinite(p.x)&&std::isfinite(p.y)&&std::isfinite(p.z)&&std::abs(p.x)<1000000&&std::abs(p.z)<1000000;}
|
|
static bool Recent(float at,float now){return std::isfinite(at)&&std::isfinite(now)&&at>=0&&now>=at&&now-at<=900000;}
|
|
static bool ValidReport(int s,int m,int b,int r,bool complete,bool contact,float at,float now){return s>0&&m>0&&b>0&&r>0&&complete&&!contact&&Recent(at,now)&&now-at<=120000;}
|
|
};
|
|
struct ERR_HQLocalCoverageRules{
|
|
'''+rules+r'''
|
|
};
|
|
struct ERR_HQSearchPatch{vector Position;float Radius=0,ObservedAt=0;};
|
|
struct ERR_HQSearchReport{int SourceId=1,MessageId=1,BeliefId=1,BeliefRevision=1;vector Position{250,0,250};float ObservedAt=1000;bool SweepCompleted=true,ContactFound=false;};
|
|
struct Coverage{
|
|
Array<ERR_HQSearchPatch*> m_Patches,m_Positives;Map<int,int>m_Replay;float m_Floor=-1;
|
|
'''+methods+r'''
|
|
};
|
|
int main(){
|
|
using R=ERR_HQLocalCoverageRules;float nan=std::numeric_limits<float>::quiet_NaN();
|
|
assert(R::RadiusValid(10)&&R::RadiusValid(40)&&!R::RadiusValid(40.01)&&!R::RadiusValid(0)&&!R::RadiusValid(nan));
|
|
assert(R::Fraction({0,0,0},10,{0,0,0},10)==1);
|
|
assert(std::abs(R::Fraction({0,0,0},100,{0,0,0},10)-.01)<.0001);
|
|
assert(R::Fraction({0,0,0},10,{20,0,0},10)==0);
|
|
assert(R::Fraction({0,0,0},10,{15,0,0},10)<.1);
|
|
assert(R::Fraction({nan,0,0},10,{0,0,0},10)==0);
|
|
Coverage c;ERR_HQSearchReport r;
|
|
assert(c.Multiplier(r.Position,10,1000)==1);
|
|
assert(c.Ingest(&r,10,2000)&&c.Count()==1);
|
|
assert(c.Multiplier(r.Position,10,2000)==.25);
|
|
assert(c.Multiplier({350,0,250},10,2000)==1); // same legacy500m cell, unseen patch stays intact
|
|
assert(c.Multiplier(r.Position,100,2000)>.99); // small patch cannot clear large hypothesis
|
|
assert(!c.Ingest(&r,10,2000));
|
|
r.MessageId=2;assert(c.Ingest(&r,10,2000));assert(c.Multiplier(r.Position,10,2000)==.25);
|
|
assert(c.Multiplier(r.Position,10,901001)==1); // observation-time expiry
|
|
r.MessageId=3;r.ContactFound=true;assert(!c.Ingest(&r,10,2000));
|
|
r.ContactFound=false;r.SweepCompleted=false;assert(!c.Ingest(&r,10,2000));
|
|
r.SweepCompleted=true;assert(!c.Ingest(&r,41,2000)&&!c.Ingest(&r,0,2000));
|
|
assert(!c.Ingest(&r,10,999)&&!c.Ingest(&r,10,121001));
|
|
assert(!c.Ingest(nullptr,10,2000));
|
|
c.Forget(r.Position,2000);assert(c.Count()==0&&c.Multiplier(r.Position,10,2000)==1);
|
|
r.ObservedAt=1500;assert(!c.Ingest(&r,10,3000)); // newer positive beats delayed negative
|
|
r.MessageId=4;r.ObservedAt=2000;assert(!c.Ingest(&r,10,3000)); // simultaneous positive wins
|
|
r.MessageId=5;r.ObservedAt=2001;assert(c.Ingest(&r,10,3000));
|
|
c.Forget(r.Position,1999);assert(c.Count()==1); // old positive cannot erase newer inspection
|
|
c.Forget({400,0,400},4000);assert(c.Count()==1); // unrelated patch stays intact
|
|
c.Forget({nan,0,0},4000);assert(c.m_Positives.Count()==3);
|
|
Coverage cap;
|
|
for(int i=0;i<140;i++){r.MessageId=i+1;r.Position={float(i*50),0,0};r.ObservedAt=1000+i;assert(cap.Ingest(&r,10,2000));}
|
|
assert(cap.Count()==128&&cap.Multiplier({0,0,0},10,2000)==1);
|
|
for(int i=0;i<140;i++)cap.Forget({float(i*50),0,0},2000+i);
|
|
assert(cap.m_Positives.Count()==128&&cap.m_Floor==2011);
|
|
r.SourceId=2;r.MessageId=1;r.Position={9000,0,9000};r.ObservedAt=2000;
|
|
assert(!cap.Ingest(&r,10,3000)); // eviction watermark protects chronology
|
|
r.MessageId=2;r.ObservedAt=2200;assert(cap.Ingest(&r,10,3000));
|
|
Coverage sources;
|
|
for(int i=1;i<=256;i++){r.SourceId=i;r.MessageId=1;r.ObservedAt=1000+i;assert(sources.Ingest(&r,10,2000));}
|
|
r.SourceId=257;assert(!sources.Ingest(&r,10,2000)&&sources.m_Replay.Count()==256);
|
|
sources.Clear();assert(sources.Count()==0&&sources.m_Replay.empty()&&sources.m_Floor==-1);
|
|
assert(sources.Ingest(&r,10,2000));
|
|
// Search destinations move to uninspected local ground while broad uncertainty survives.
|
|
Coverage selection;ERR_HQSearchReport done;vector chosen;
|
|
assert(selection.Select(done.Position,90,done.Position,2000,chosen));assert(chosen.x==250&&chosen.z==250);
|
|
assert(selection.Ingest(&done,10,2000));
|
|
assert(selection.Select(done.Position,90,done.Position,2000,chosen));
|
|
assert(vector::DistanceXZ(chosen,done.Position)==30&&selection.Multiplier(chosen,10,2000)==1);
|
|
Array<vector> reserved;reserved.Insert(chosen);
|
|
assert(selection.Select(done.Position,90,done.Position,2000,chosen,&reserved));
|
|
assert(vector::DistanceXZ(chosen,reserved[0])>=60&&vector::DistanceXZ(chosen,done.Position)<=90);
|
|
assert(!selection.Select(done.Position,10,done.Position,2000,chosen));
|
|
assert(selection.Select(done.Position,10,done.Position,901001,chosen)); // expired visit reopens
|
|
assert(!selection.Select(done.Position,111,done.Position,2000,chosen));
|
|
assert(!selection.Select(done.Position,nan,done.Position,2000,chosen));
|
|
for(int i=0;i<32;i++)reserved.Insert(done.Position);
|
|
assert(!selection.Select(done.Position,90,done.Position,2000,chosen,&reserved));
|
|
}
|
|
''')
|
|
print('PASS: actual10/40m local negative patches, same-cell hidden-ground preservation, proportional residual belief, replay, chronology, physical-report guards, caps and cleanup')
|