90 lines
6.4 KiB
Python
90 lines
6.4 KiB
Python
"""Execute the actual local evidence cache and priority/radio intake methods."""
|
|
import re
|
|
from policy_harness import ROOT,body,run
|
|
base=ROOT/'addons/core/Scripts/Game/ERR/10_AI'
|
|
s=(base/'ERR_HQLocalAwareness.c').read_text().split('modded class')[0]
|
|
h=(base/'ERR_HQLocalHandoff.c').read_text().split('class ERR_HQObservationHistory')[1]
|
|
h='class ERR_HQObservationHistory'+h
|
|
q='\n'.join(body(base/'ERR_HQ.c',sig) for sig in ['protected void ContactReport(', 'protected void QueueReports('])
|
|
def trans(s):
|
|
s=s.replace('protected ','').replace('ref ','').replace('class ','struct ').replace('\n}\n','\n};\n')
|
|
s=s.replace('array<ERR_HQReport>', 'Array<ERR_HQReport*>').replace('array<IEntity>','Array<IEntity>').replace('array<float>','Array<float>')
|
|
for t in ['ERR_HQReport','ERR_HQObservationHistory','ERR_HQUnit','ERR_HQRoute','SCR_AIGroup']:
|
|
s=re.sub(r'\b'+t+r' (\w+)',t+r'* \1',s)
|
|
s=s.replace('foreach (','for (').replace('null','nullptr')
|
|
for c in ['ERR_ForceRules','ERR_HQMemoryRules','ERR_HQLocalAwareness','Math']:
|
|
s=s.replace(c+'.',c+'::')
|
|
for c in ['known','report','source','history','unit','route']:
|
|
s=re.sub(r'\b'+c+r'\.',c+'->',s)
|
|
s=s.replace('m_Sound.', 'm_Sound->').replace('m_Visual[i].','m_Visual[i]->')
|
|
s=s.replace('unit->LocalAwareness.','unit->LocalAwareness->').replace('unit->SentObservations.','unit->SentObservations->')
|
|
s=s.replace('m_Mode.Settings().Runtime','m_Mode.Settings().Runtime')
|
|
s=s.replace('ERR_HQReport* m_Sound;', 'ERR_HQReport* m_Sound=nullptr;').replace('float m_NextSound;', 'float m_NextSound=0;')
|
|
return s.replace('unit->Task.', 'unit->Task->')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <vector>
|
|
using string=std::string;
|
|
struct Actor{};using IEntity=Actor*;
|
|
struct SCR_AIGroup:Actor{};
|
|
struct vector{float v[3]={0,0,0};float& operator[](int i){return v[i];}};
|
|
vector Vector(float x,float y,float z){return {{x,y,z}};}
|
|
namespace Math{float Floor(float f){return std::floor(f);}}
|
|
namespace ERR_ForceRules{bool Finite(float f){return std::isfinite(f);}}
|
|
namespace ERR_HQMemoryRules{bool PositionValid(vector p){return std::isfinite(p[0])&&std::isfinite(p[1])&&std::isfinite(p[2]);}}
|
|
template<class T>struct Array:std::vector<T>{void Clear(){this->clear();}int Count(){return this->size();}
|
|
void Insert(T x){this->push_back(x);}void Remove(int i){this->erase(this->begin()+i);}int Find(T x){auto p=std::find(this->begin(),this->end(),x);return p==this->end()?-1:p-this->begin();}};
|
|
struct ERR_HQReport{IEntity Target=nullptr;SCR_AIGroup* Source=nullptr;vector Position;float ObservedAt=0;
|
|
int MessageId=0;string TravelMode="unknown";string Weapon="unknown";
|
|
ERR_HQReport* Snapshot(){return new ERR_HQReport(*this);}string Description(float){return TravelMode;}};
|
|
'''+trans(h)+trans(s)+r'''
|
|
struct ERR_HQRoute{int MessageId=0;string Payload;};
|
|
struct Task{float ArrivedAt=0;};
|
|
struct ERR_HQUnit{SCR_AIGroup* Group=nullptr;IEntity Radio=nullptr;ERR_HQLocalAwareness own;
|
|
ERR_HQLocalAwareness* LocalAwareness=&own;ERR_HQObservationHistory hist;ERR_HQObservationHistory* SentObservations=&hist;
|
|
ERR_HQRoute* Uplink=nullptr;ERR_HQReport* PendingReport=nullptr;struct Task* Task=nullptr;
|
|
float NextContact=0,NextReport=0,NextStatus=0;bool status=false;};
|
|
struct Settings{int Runtime(string){return 10;}};
|
|
struct Mode{struct Settings Settings(){return {};}};
|
|
struct Journal{int id=0;int Message(){return ++id;}void Write(string,string,int){}};
|
|
struct HQ{Mode m_Mode;Journal m_Journal;bool network=true,occupied=false;
|
|
ERR_HQRoute* ObservationRoute(ERR_HQUnit*){return network?new ERR_HQRoute:nullptr;}
|
|
bool OriginOccupied(IEntity,ERR_HQUnit*){return occupied;}
|
|
void StatusReport(ERR_HQUnit* u,float now){if(!u->Uplink&&now>=u->NextStatus){u->Uplink=new ERR_HQRoute;u->status=true;u->NextStatus=now+180000;}}
|
|
void Observe(ERR_HQUnit*,float){} void ObserveBody(ERR_HQUnit*,float){} void Sweep(ERR_HQUnit*,float){}
|
|
'''+trans(q)+r'''
|
|
};
|
|
int main(){
|
|
SCR_AIGroup group;Actor enemy;ERR_HQLocalAwareness cache;ERR_HQObservationHistory history;
|
|
ERR_HQReport r;r.Target=&enemy;r.Source=&group;r.Position=Vector(20,0,40);r.ObservedAt=100;r.Weapon="rifle";
|
|
cache.Remember(&r,100);r.Position[0]=900;r.Weapon="launcher";
|
|
auto held=cache.Peek(1200,&history);assert(held&&held->Position[0]==20&&held->Weapon=="rifle"&&held->ObservedAt==100);
|
|
assert(cache.Peek(30100,&history)&&!cache.Peek(30101,&history)); // original time, never refresh on reads
|
|
r.ObservedAt=32000;cache.Remember(&r,32000);assert(!cache.CaptureDue(&enemy,32999)&&cache.CaptureDue(&enemy,33000));
|
|
held=cache.Peek(33000,&history);history.Queued(held->Target,held->ObservedAt,33000);cache.Queued(held,33000);
|
|
cache.Remember(&r,33000);assert(!cache.Peek(33000,&history)); // same observation cannot replay
|
|
cache.Hear(&group,Vector(12,8,-1),34000,34000);auto sound=cache.Peek(34000,&history);
|
|
assert(sound&&!sound->Target&&sound->Source==&group&&sound->Position[0]==75&&sound->Position[2]==-75);
|
|
assert(sound->TravelMode=="heard gunfire"&&sound->Weapon=="unknown");
|
|
cache.Queued(sound,34000);cache.Hear(&group,Vector(800,0,0),35000,35000);
|
|
assert(!cache.Peek(63999,&history)&&cache.Peek(64000,&history));
|
|
cache.Clear();assert(!cache.Peek(64000,&history));
|
|
Actor targets[40];for(int i=0;i<40;i++){r.Target=&targets[i];r.ObservedAt=70000+i;cache.Remember(&r,70000+i);}
|
|
assert(cache.m_Visual.Count()==16);cache.Clear();
|
|
HQ h;ERR_HQUnit u;u.Group=&group;r.Target=&enemy;r.ObservedAt=1000;
|
|
u.own.Remember(&r,1000);h.QueueReports(&u,2000);assert(u.PendingReport&&!u.status&&u.PendingReport->ObservedAt==1000);
|
|
auto inFlight=u.Uplink;r.ObservedAt=3000;u.own.Remember(&r,3000);h.QueueReports(&u,4000);assert(u.Uplink==inFlight);
|
|
u.Uplink=nullptr;u.PendingReport=nullptr;r.ObservedAt=16000;u.own.Remember(&r,16000);
|
|
h.QueueReports(&u,16000);assert(u.status&&!u.PendingReport); // bounded status delay under continuous contacts
|
|
u.Uplink=nullptr;u.status=false;h.QueueReports(&u,17000);assert(u.PendingReport&&u.PendingReport->ObservedAt==16000);
|
|
u.Uplink=nullptr;u.PendingReport=nullptr;r.ObservedAt=23000;u.own.Remember(&r,23000);
|
|
h.network=false;h.ContactReport(&u,23000);assert(!u.Uplink&&u.own.Peek(24000,&u.hist));
|
|
h.network=true;h.ContactReport(&u,24000);assert(!u.Uplink); // failed-route retry bounded
|
|
h.ContactReport(&u,28000);assert(u.PendingReport&&u.PendingReport->ObservedAt==23000);
|
|
}
|
|
''')
|
|
print('PASS: captured sighting retention, immutable details, expiry, replay rejection, bounded sound uncertainty/cooldown, radio loss, priority and overdue-status fairness')
|