Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -11,31 +11,35 @@ assert legacy in original or 'QueueReports(unit, now);' in original
|
||||
|
||||
def cpp(s):
|
||||
s=s.replace('ERR_HQLocalAwareness.Priority','ERR_HQLocalAwareness::Priority').replace('protected ','').replace('array<ref ERR_HQVisualContact>','Array<ERR_HQVisualContact*>')
|
||||
for t in ('ERR_HQAmmoSnapshot','ERR_HQUnit','ERR_HQVisualContact','ERR_HQRoute','ERR_HQReport','ERR_HQStatusReport','ERR_HQTask','ERR_HQSearchReport'):
|
||||
for t in ('ERR_HQAmmoSnapshot','ERR_HQMountedAmmoSnapshot','ERR_HQUnit','ERR_HQVisualContact','ERR_HQRoute','ERR_HQReport','ERR_HQStatusReport','ERR_HQTask','ERR_HQSearchReport','IEntity'):
|
||||
s=re.sub(r'\b'+t+r' (?=\w)',t+'* ',s)
|
||||
s=s.replace('ERR_HQRoute* route;', 'ERR_HQRoute* route=nullptr;')
|
||||
s=s.replace('ERR_HQReport* report;','ERR_HQReport* report = nullptr;')
|
||||
for v in ('unit','route','report','seen','task','stock'):
|
||||
for v in ('unit','route','report','seen','task','stock','mounted','actor'):
|
||||
s=s.replace(v+'.',v+'->')
|
||||
for v in ('Utility','ReportSelection','StatusSampler','Group','Task'):
|
||||
s=s.replace('->'+v+'.','->'+v+'->')
|
||||
s=s.replace('m_Mode.Settings().Runtime(', 'm_Mode->Settings()->Runtime(')
|
||||
s=s.replace('ERR_HQPursuitRules.', 'ERR_HQPursuitRules::')
|
||||
s=s.replace('ERR_HQPursuitRules.', 'ERR_HQPursuitRules::').replace('ERR_Actors.', 'ERR_Actors::').replace('CompartmentAccessComponent.', 'CompartmentAccessComponent::')
|
||||
s=s.replace('ERR_HQObservation.','ERR_HQObservation::').replace('vector.DistanceXZ','vector::DistanceXZ').replace('string.Format','Format')
|
||||
return s.replace('AmmoState.', 'AmmoState->').replace('SecuritySite.IsEmpty()', 'SecuritySite.empty()')
|
||||
return s.replace('MountedAmmo.', 'MountedAmmo->').replace('AmmoState.', 'AmmoState->').replace('SecuritySite.IsEmpty()', 'SecuritySite.empty()')
|
||||
policy_path=Path(os.environ.get('ERR_AMMO_REPORTING_ROOT', ROOT))/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoReporting.c'
|
||||
policy=cpp(policy_path.read_text()).replace('class ERR_HQAmmoReporting','struct ERR_HQAmmoReporting').rstrip()+';'
|
||||
methods='\n'.join(cpp(body(P,s)) for s in ('protected void Observe(', 'protected void ObserveBody(', 'protected void StatusReport(', 'protected void Sweep(', 'protected void QueueReports('))
|
||||
methods='\n'.join(cpp(body(P,s)) for s in ('protected void Observe(', 'protected void ObserveBody(', 'protected void StatusReport(', 'protected bool SweepPresent(', 'protected void Sweep(', 'protected void QueueReports('))
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
using string=std::string;
|
||||
template<class... T>string Format(T... args){return "diagnostic";}
|
||||
struct vector{float x=0,z=0;static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
|
||||
template<class T>struct Array:std::vector<T>{bool IsEmpty(){return this->empty();}};
|
||||
struct Group{vector position;vector GetOrigin(){return position;}};
|
||||
struct Group{vector position;int originReads=0;vector GetOrigin(){originReads++;return position;}};
|
||||
struct IEntity{vector position;bool fit=true,attributed=true,aboard=false;int originReads=0;vector GetOrigin(){originReads++;return position;}};
|
||||
struct ERR_Actors{static bool CanFight(IEntity* actor){return actor&&actor->fit;}};
|
||||
struct CompartmentAccessComponent{static void* GetVehicleIn(IEntity* actor){return actor->aboard?actor:nullptr;}};
|
||||
struct Settings{int interval=5;int Runtime(string){return interval;}};
|
||||
struct Mode{struct Settings settings;struct Settings* Settings(){return &settings;}};
|
||||
struct ERR_HQVisualContact{void* Entity=this;vector Position;float ObservedAt=0;};
|
||||
@@ -47,15 +51,21 @@ struct ERR_HQObservation{static void Gather(Utility* u,float now,Array<ERR_HQVis
|
||||
struct Selection{int attempts=0;ERR_HQVisualContact* Select(Array<ERR_HQVisualContact*>& a){return a.front();}void Queued(void*){attempts++;}};
|
||||
struct ERR_HQReport{int MessageId=0;void* Target=nullptr;Group* Source=nullptr;vector Position;float ObservedAt=0;
|
||||
string Description(float){return "contact";}ERR_HQReport* Snapshot(){return new ERR_HQReport(*this);}};
|
||||
struct ERR_HQPursuitRules{static bool Finite(float value){return std::isfinite(value);}};
|
||||
struct ERR_HQAmmoSnapshot{bool Complete=false;int SmallArmsRounds=0,SmallArmsChambers=0,SmallArmsMuzzles=0,Members=0;bool Valid(int){return false;}};
|
||||
struct ERR_HQStatusReport{ERR_HQAmmoSnapshot* AmmoState=nullptr;int MessageId=0,Count=4,Fit=4,ConfirmedLosses=0;string SourceId="own-1";float ObservedAt=0;};
|
||||
struct ERR_HQPursuitRules{static bool Finite(float value){return std::isfinite(value);}static bool PositionValid(vector p){return Finite(p.x)&&Finite(p.z);}};
|
||||
struct ERR_HQAmmoSnapshot{bool Complete=false,MineComplete=false;int ReadyMines=0,MineMembers=0,SmallArmsRounds=0,SmallArmsChambers=0,SmallArmsMuzzles=0,Members=0;bool Valid(int){return false;}};
|
||||
struct ERR_HQMountedAmmoSnapshot{bool Complete=false;int Weapons=0,Muzzles=0,Magazines=0,Rounds=0,Capacity=0,Chambers=0,LowMagazines=0;
|
||||
'''+body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c',' bool Valid(')+r'''
|
||||
};
|
||||
struct ERR_HQStatusReport{ERR_HQMountedAmmoSnapshot* MountedAmmo=nullptr;ERR_HQAmmoSnapshot* AmmoState=nullptr;int MessageId=0,Count=4,Fit=4,ConfirmedLosses=0;string SourceId="own-1";float ObservedAt=0;};
|
||||
'''+policy+r'''
|
||||
struct Sampler{int observations=0,samples=0;bool valid=true;void ObserveDeaths(){observations++;}
|
||||
struct Sampler{IEntity original;Group* group=nullptr;int strength=1;bool bound=true;IEntity* member=&original;
|
||||
bool OwnsRoster(Group* requested){return bound&&group&&requested==group;}int InitialStrength(){return strength;}
|
||||
IEntity* InitialMember(int slot){return slot==0?member:nullptr;}bool CanAttribute(int slot,IEntity* actor,bool observeLoss){assert(!observeLoss);return slot==0&&actor==&original&&actor->attributed;}
|
||||
int observations=0,samples=0;bool valid=true;void ObserveDeaths(){observations++;}
|
||||
ERR_HQStatusReport* Sample(float now,int id,void*){samples++;if(!valid)return nullptr;auto r=new ERR_HQStatusReport;r->MessageId=id;r->ObservedAt=now;return r;}};
|
||||
struct ERR_HQSearchReport{int SourceId=0,MessageId=0,WatchMessageId=0,WatchRequestId=0,BeliefId=0,BeliefRevision=0;
|
||||
string WatchSourceId;vector Position;float ObservedAt=0;bool SweepCompleted=false,ContactFound=false;};
|
||||
struct ERR_HQTask{std::string SecuritySite;bool RoadIntercept=false,SweepQueued=false;float ArrivedAt=-1;vector Destination;
|
||||
struct ERR_HQTask{int OperationId=0;std::string SecuritySite;bool RoadIntercept=false,SweepQueued=false;float ArrivedAt=-1;vector Destination;
|
||||
string WatchSourceId;int WatchMessageId=0,WatchRequestId=0,BeliefId=1,BeliefRevision=2;};
|
||||
struct ERR_HQRoute{Group* LocalSource=nullptr;int MessageId=0;string Payload;};
|
||||
struct Network{bool available=true;int routes=0;ERR_HQRoute* Route(void*){routes++;return available?new ERR_HQRoute:nullptr;}};
|
||||
@@ -63,16 +73,17 @@ struct Bodies{bool visible=false;bool Observe(Group*,float now,ERR_HQReport*& r)
|
||||
struct Journal{int seq=0;int Message(){return ++seq;}void Write(string,string,int){}};
|
||||
struct History{bool CanQueue(void*,float,float){return true;}void Queued(void*,float,float){}};
|
||||
struct ERR_HQLocalAwareness{static bool Priority(float,float,float){return false;}};
|
||||
struct ERR_HQUnit{float NextContact=0;History SentObservations;int Id=1;struct Group ownGroup;struct Utility ownUtility;Selection selection;Sampler sampler;
|
||||
struct ERR_HQUnit{bool ExecutionTurn=true;float NextContact=0;History SentObservations;int Id=1;struct Group ownGroup;struct Utility ownUtility;Selection selection;Sampler sampler;
|
||||
struct Group* Group=&ownGroup;struct Utility* Utility=&ownUtility;void* Radio=this;
|
||||
Selection* ReportSelection=&selection;Sampler* StatusSampler=&sampler;
|
||||
ERR_HQRoute* Uplink=nullptr;ERR_HQReport* PendingReport=nullptr;ERR_HQStatusReport* PendingStatus=nullptr;ERR_HQSearchReport* PendingSweep=nullptr;
|
||||
ERR_HQAmmoReporting AmmoReporting;
|
||||
float NextReport=0,NextStatus=0;bool fighting=false,eligible=true;
|
||||
ERR_HQTask* Task=nullptr;void* Assignment=nullptr;void* Waypoint=nullptr;
|
||||
ERR_HQUnit(){sampler.group=&ownGroup;}
|
||||
bool Eligible(Mode*){return eligible;}
|
||||
};
|
||||
struct HQ{void ContactReport(ERR_HQUnit*,float){}
|
||||
struct HQ{bool m_Coordinated=false;void QueueExecution(ERR_HQUnit*,float){}void ContactReport(ERR_HQUnit*,float){}
|
||||
Mode mode;Mode* m_Mode=&mode;Network m_Network;Bodies m_Bodies;Journal m_Journal;
|
||||
bool OriginOccupied(void*,ERR_HQUnit*){return false;}
|
||||
ERR_HQRoute* ObservationRoute(ERR_HQUnit* u){return u->Radio?m_Network.Route(u->Radio):nullptr;}
|
||||
@@ -99,6 +110,36 @@ Stats Simulate(bool legacy){HQ h;ERR_HQUnit u;Stats counts;float due=-1;
|
||||
assert(u.sampler.observations==601);return counts;
|
||||
}
|
||||
int main(){
|
||||
// Only original physical presence can accumulate negative-search dwell.
|
||||
// A group left at the spawn anchor cannot hide arrival or fabricate it.
|
||||
{HQ h;ERR_HQUnit u;ERR_HQTask task;u.Task=&task;u.Assignment=&task;u.Waypoint=&task;u.ownUtility.visible=false;
|
||||
u.ownGroup.position={1000,1000};u.sampler.original.position={40,0};
|
||||
h.Sweep(&u,1000);assert(task.ArrivedAt==1000&&!u.Uplink&&u.ownGroup.originReads==0);
|
||||
h.Sweep(&u,30999);assert(!u.PendingSweep);h.Sweep(&u,31000);
|
||||
assert(u.PendingSweep&&u.PendingSweep->ObservedAt==31000&&u.PendingSweep->Position.x==task.Destination.x);
|
||||
auto report=u.PendingSweep;u.sampler.original.position={900,900};h.Sweep(&u,32000);
|
||||
assert(u.PendingSweep==report&&report->ObservedAt==31000&&report->Position.x==0);}
|
||||
for(int invalid=0;invalid<10;invalid++){
|
||||
HQ h;ERR_HQUnit u;ERR_HQTask task;task.ArrivedAt=0;u.Task=&task;u.Assignment=&task;u.Waypoint=&task;u.ownUtility.visible=false;
|
||||
IEntity replacement;Group foreign;
|
||||
if(invalid==0){u.sampler.original.position={40.01f,0};}
|
||||
if(invalid==1){u.sampler.original.fit=false;}
|
||||
if(invalid==2){u.sampler.original.attributed=false;}
|
||||
if(invalid==3){u.sampler.original.aboard=true;}
|
||||
if(invalid==4){u.sampler.member=&replacement;}
|
||||
if(invalid==5){u.sampler.bound=false;}
|
||||
if(invalid==6){u.sampler.group=&foreign;}
|
||||
if(invalid==7){u.sampler.strength=33;}
|
||||
if(invalid==8){u.sampler.original.position.x=std::numeric_limits<float>::quiet_NaN();}
|
||||
if(invalid==9){u.eligible=false;}
|
||||
h.Sweep(&u,31000);assert(task.ArrivedAt==-1&&!u.PendingSweep&&!u.Uplink&&u.ownGroup.originReads==0);
|
||||
if(invalid>0&&invalid<8){assert(u.sampler.original.originReads==0&&replacement.originReads==0);}
|
||||
}
|
||||
// An eligibility interruption restarts the full dwell instead of preserving credit.
|
||||
{HQ h;ERR_HQUnit u;ERR_HQTask task;u.Task=&task;u.Assignment=&task;u.Waypoint=&task;u.ownUtility.visible=false;
|
||||
h.Sweep(&u,1000);u.eligible=false;h.Sweep(&u,20000);assert(task.ArrivedAt==-1);
|
||||
u.eligible=true;h.Sweep(&u,31000);assert(task.ArrivedAt==31000&&!u.PendingSweep);
|
||||
h.Sweep(&u,60999);assert(!u.PendingSweep);h.Sweep(&u,61000);assert(u.PendingSweep&&u.PendingSweep->ObservedAt==61000);}
|
||||
auto before=Simulate(true);assert(before.sight==100&&before.status==0);
|
||||
auto after=Simulate(false);assert(after.status>=4&&after.sight>=95);
|
||||
HQ h;ERR_HQUnit u;Stats counts;
|
||||
@@ -133,8 +174,8 @@ int main(){
|
||||
Finish(u,counts);u.ownUtility.visible=false;u.fighting=true;task.ArrivedAt=0;
|
||||
h.QueueReports(&u,90000);assert(task.ArrivedAt==-1&&!u.PendingSweep);
|
||||
u.fighting=false;h.QueueReports(&u,91000);assert(task.ArrivedAt==91000);
|
||||
u.ownGroup.position.x=100;h.QueueReports(&u,92000);assert(task.ArrivedAt==-1&&!u.PendingSweep);
|
||||
u.ownGroup.position.x=0;h.QueueReports(&u,93000);assert(task.ArrivedAt==93000);
|
||||
u.sampler.original.position.x=100;h.QueueReports(&u,92000);assert(task.ArrivedAt==-1&&!u.PendingSweep);
|
||||
u.sampler.original.position.x=0;h.QueueReports(&u,93000);assert(task.ArrivedAt==93000);
|
||||
h.QueueReports(&u,122000);assert(!u.PendingSweep);h.QueueReports(&u,123000);assert(u.PendingSweep);
|
||||
// Route loss or an unavailable status sampler must not fabricate a report or block useful contacts.
|
||||
HQ lost;ERR_HQUnit v;lost.m_Network.available=false;lost.QueueReports(&v,0);
|
||||
|
||||
Reference in New Issue
Block a user