Files
EscapistsReintroduced/tests/test_hq_local_awareness.py
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

109 lines
8.2 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_Alarm.', 'm_Alarm->').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_Alarm;', 'ERR_HQReport* m_Alarm=nullptr;').replace('float m_NextAlarm;', 'float m_NextAlarm=0;')
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{bool ExecutionTurn=true;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{bool m_Coordinated=false;void QueueExecution(ERR_HQUnit*,float){}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));
// Alarm retains its own slot and timestamp; it cannot replace gunfire/visual
// evidence or use multiple squad members to replay an already queued event.
cache.HearAlarm(&group,Vector(4,7,9),65000,65001);
auto alarm=cache.Peek(65001,&history);assert(alarm&&!alarm->Target&&alarm->TravelMode=="heard vehicle alarm");
assert(alarm->Position[0]==75&&alarm->Position[2]==75&&alarm->ObservedAt==65000);
cache.HearAlarm(&group,Vector(900,0,900),64000,65001);assert(cache.Peek(65001,&history)->Position[0]==75);
cache.Hear(&group,Vector(250,0,250),65001,65001);assert(cache.Peek(65001,&history)->TravelMode=="heard gunfire");
r.ObservedAt=65002;cache.Remember(&r,65002);assert(cache.Peek(65002,&history)->Target==&enemy);
auto visual=cache.Peek(65002,&history);cache.Queued(visual,65002);
auto gunfire=cache.Peek(65002,&history);assert(gunfire->TravelMode=="heard gunfire");cache.Queued(gunfire,65002);
assert(cache.Peek(65002,&history)->TravelMode=="heard vehicle alarm");cache.Queued(alarm,65002);
cache.HearAlarm(&group,Vector(4,7,9),65000,65003);assert(!cache.m_Alarm); // duplicate native receiver
cache.HearAlarm(&group,Vector(4,7,9),66000,66000);assert(!cache.Peek(95001,&history));
assert(cache.Peek(95002,&history)->ObservedAt==66000);
assert(!cache.Peek(96001,&history)); // receipt never renews observation age
cache.Clear();cache.HearAlarm(nullptr,Vector(0,0,0),1,1);cache.HearAlarm(&group,Vector(0,0,0),2,1);
cache.HearAlarm(&group,Vector(NAN,0,0),1,1);assert(!cache.Peek(1,&history));
cache.HearAlarm(&group,Vector(4,7,9),1,1);assert(cache.Peek(1,&history));cache.Clear();
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')