Files
EscapistsReintroduced/tests/test_hq_operations.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

390 lines
32 KiB
Python

"""Execute real detached operation manager; no native entities or hidden players exist in this harness."""
from policy_harness import ROOT, run
import re
P=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQOperations.c'
source=P.read_text()
judgment=(P.parent/'ERR_HQSquadJudgment.c').read_text()
def cpp(s):
s=s.replace('protected ','').replace('class ','struct ').replace('ref ','')
s=s.replace('array<ERR_HQOperationSlot> m_Slots = {}','Array<ERR_HQOperationSlot*> m_Slots')
s=s.replace('array<ERR_HQSquadJudgment> m_Squads = {}','Array<ERR_HQSquadJudgment*> m_Squads')
s=s.replace('array<ERR_HQHypothesis> hypotheses','Array<ERR_HQHypothesis*>* hypotheses')
s=s.replace('array<ERR_HQOperationForce> forces','Array<ERR_HQOperationForce*>* forces')
types=('ERR_HQOperationForce','ERR_HQOperationSlot','ERR_HQOperationChoice','ERR_HQTask','ERR_HQExecutionReport','ERR_HQBelief','ERR_HQHypothesis','ERR_HQSquadJudgment')
for typ in types:
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
s=s.replace('struct ERR_HQOperationForce*','struct ERR_HQOperationForce').replace('struct ERR_HQOperationSlot*','struct ERR_HQOperationSlot').replace('struct ERR_HQOperationChoice*','struct ERR_HQOperationChoice')
s=s.replace('struct ERR_HQSquadJudgment*','struct ERR_HQSquadJudgment')
for typ in ('ERR_HQOperationRules','ERR_HQPursuitRules','ERR_HQMemoryRules','ERR_HQExecutionState','ERR_HQOperationRole','ERR_HQPursuitRole','ERR_HQMemoryKind'):
s=s.replace(typ+'.',typ+'::')
s=s.replace('Math.','Math::').replace('vector.','vector::')
for name in ('report','force','slot','other','belief','hypothesis','best','task','support','hypotheses','forces','Task','Report','PreviousTask','PreviousReport','squad'):
s=s.replace(name+'.',name+'->')
s=s.replace('foreach (ERR_HQOperationSlot* slot : m_Slots)','for (ERR_HQOperationSlot* slot : m_Slots)').replace('foreach (ERR_HQOperationSlot* other : m_Slots)','for (ERR_HQOperationSlot* other : m_Slots)')
s=s.replace('foreach (ERR_HQSquadJudgment* squad : m_Squads)','for (ERR_HQSquadJudgment* squad : m_Squads)')
s=s.replace('foreach (ERR_HQOperationForce* other : forces)','for (ERR_HQOperationForce* other : *forces)')
s=s.replace('foreach (ERR_HQOperationForce* force : forces)','for (ERR_HQOperationForce* force : *forces)')
s=s.replace('foreach (ERR_HQHypothesis* hypothesis : hypotheses)','for (ERR_HQHypothesis* hypothesis : *hypotheses)')
s=re.sub(r'\bnull\b','nullptr',s).replace('"0 0 1"','Vector(0,0,1)')
s=re.sub(r'^(})$',r'\1;',s,flags=re.M)
# Native script zero-initializes fields and locals; C++ does not.
s=re.sub(r'\b(int|float|bool) (\w+);',r'\1 \2{};',s)
s=re.sub(r'\b(ERR_HQ\w+\*) (\w+);',r'\1 \2=nullptr;',s)
return s
run(r'''
#include <cassert>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <limits>
using string=std::string;
struct vector{float x=0,y=0,z=0;float&operator[](int i){return i==0?x:i==1?y:z;}
vector operator+(vector b)const{return {x+b.x,y+b.y,z+b.z};}vector operator-(vector b)const{return {x-b.x,y-b.y,z-b.z};}
vector operator*(float n)const{return {x*n,y*n,z*n};}float Length()const{return std::sqrt(x*x+y*y+z*z);}
vector Normalized()const{return *this*(1/Length());}void Normalize(){*this=Normalized();}
static float DistanceXZ(vector a,vector b){return std::hypot(a.x-b.x,a.z-b.z);}};
vector Vector(float x,float y,float z){return {x,y,z};}
struct Math{static float Clamp(float v,float a,float b){return std::clamp(v,a,b);}static float Max(float a,float b){return std::max(a,b);}};
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}void Insert(T v){this->push_back(v);}void Clear(){this->clear();}};
enum ERR_HQExecutionState{QUEUED,RECEIVED,TRAVELLING,ARRIVED,ACTING,COMPLETED,BLOCKED,FAILED,SUPERSEDED,CONTACT_CHANGED};
enum class ERR_HQOperationRole{OBSERVE,FIX,FLANK,BLOCK,SEARCH,RESERVE,WITHDRAW};
enum class ERR_HQPursuitRole{SEARCH,INTERCEPT,CONTAIN};
enum class ERR_HQMemoryKind{SIGHTING,GUNFIRE};
struct ERR_HQPursuitRules{static bool Finite(float v){return std::isfinite(v);}static bool Actionable(float c,float a,float r){return Finite(c)&&Finite(a)&&Finite(r)&&c>=.15&&c<=1&&a>=0&&r>=0&&r<=2000;}static float SearchRadius(float r){return std::clamp(r,45.f,110.f);}};
struct ERR_HQMemoryRules{static bool PositionValid(vector p){return std::isfinite(p.x)&&std::isfinite(p.y)&&std::isfinite(p.z);}static bool DeliveryFresh(float at,float now){return std::isfinite(at)&&std::isfinite(now)&&at>=0&&now>=at&&now-at<=120000;}};
struct ERR_HQTask{int OperationId=0,OperationRevision=0,ExecutionId=0,HypothesisId=0,BeliefId=0,BeliefRevision=0,SupportSourceId=0;ERR_HQOperationRole OperationRole=ERR_HQOperationRole::SEARCH;ERR_HQPursuitRole Role=ERR_HQPursuitRole::SEARCH;vector Destination,SupportPosition;float QueuedAt=0,ExpiresAt=0,SearchRadiusMetres=0,SupportUntil=0;bool Maneuver=false;string ExpectedOutcome;ERR_HQTask*ReplacesTask=nullptr;};
struct ERR_HQExecutionReport{int OperationId=0,OperationRevision=0,ExecutionId=0,BeliefId=0,BeliefRevision=0,HypothesisId=0,SourceId=0,MessageId=0,ReportSequence=0;float ObservedAt=0,ContactObservedAt=-1,SupportUntil=0,SearchedRadius=0,DistanceRemaining=0;ERR_HQExecutionState State=RECEIVED;vector Position,ContactPosition,SupportPosition;bool HasContact=false,SupportReady=false,PhysicallyArrived=false;string Detail;ERR_HQExecutionReport*Snapshot(){return new ERR_HQExecutionReport(*this);}};
struct ERR_HQBelief{int BeliefId=1,BeliefRevision=1;ERR_HQMemoryKind Kind=ERR_HQMemoryKind::SIGHTING;void*Report=this;float Confidence=1,ObservedAt=0,Radius=80;vector Position{1000,0,1000};};
struct ERR_HQHypothesis{int Id=1,BeliefId=1,BeliefRevision=1,ReportId=1;vector Position{1100,0,1000};float Weight=.6,Radius=40,EarliestSeconds=20,LatestSeconds=300;bool OnRoad=false;};
'''+cpp(judgment)+cpp(source)+r'''
ERR_HQOperationForce force(int id){ERR_HQOperationForce f;f.Id=id;f.Position={950,0,1000};f.Strength=4;f.Fit=4;f.ObservedAt=0;return f;}
ERR_HQTask task(int operation=1,int revision=1){ERR_HQTask t;t.OperationId=operation;t.OperationRevision=revision;t.BeliefId=operation;t.BeliefRevision=revision;t.HypothesisId=1;t.QueuedAt=1000;t.ExpiresAt=121000;t.Destination={1100,0,1000};return t;}
ERR_HQExecutionReport report(ERR_HQTask&t,int source,int message,int sequence,ERR_HQExecutionState state,float at){ERR_HQExecutionReport r;r.SourceId=source;r.OperationId=t.OperationId;r.OperationRevision=t.OperationRevision;r.BeliefId=t.BeliefId;r.BeliefRevision=t.BeliefRevision;r.ExecutionId=t.ExecutionId;r.HypothesisId=t.HypothesisId;r.MessageId=message;r.ReportSequence=sequence;r.ObservedAt=at;r.State=state;r.Position={1000,0,1000};r.DistanceRemaining=50;return r;}
int main(){
ERR_HQOperations manager;ERR_HQBelief b;auto f=force(1);auto t=task();
assert(manager.Available(&f,&b,1000));assert(manager.Queue(1,&t)&&t.ExecutionId>0&&manager.Count(1,1000)==1);
int assignedExecution=t.ExecutionId;assert(!manager.Queue(1,&t)&&t.ExecutionId==assignedExecution&&!manager.Find(1)->PreviousTask);
// A delivered status sampled before the pending order cannot free its reservation.
assert(!manager.Available(&f,&b,1001));
f.OperationId=1;f.ExecutionId=t.ExecutionId;assert(!manager.Available(&f,&b,2000));
auto r=report(t,1,1,1,TRAVELLING,2000);assert(manager.Accept(&r,3000));
assert(!manager.Accept(&r,3001));r.MessageId=2;assert(!manager.Accept(&r,3001));
r.ReportSequence=2;r.OperationRevision=9;assert(!manager.Accept(&r,3001));r.OperationRevision=1;
r.ObservedAt=4000;assert(!manager.Accept(&r,3001));r.ObservedAt=3000;
assert(manager.Accept(&r,3001));
r.Position={9000,0,9000};assert(manager.Find(1)->Report->Position.x==1000); // detached acceptance
r=report(t,1,3,3,BLOCKED,4000);assert(manager.Accept(&r,5000)&&manager.Find(1)->Failures==1);
assert(!manager.Available(&f,&b,19000)&&manager.Available(&f,&b,20000));
auto next=task();next.QueuedAt=20000;next.ExpiresAt=140000;assert(manager.Queue(1,&next));f.ExecutionId=next.ExecutionId;
r=report(next,1,4,1,FAILED,21000);assert(manager.Accept(&r,22000)&&manager.Find(1)->Failures==2);
auto stale=report(t,1,5,4,COMPLETED,22000);assert(!manager.Accept(&stale,23000));
r=report(next,1,5,2,TRAVELLING,23000);assert(!manager.Accept(&r,24000)); // terminal cannot resurrect
manager.Release(1,t.ExecutionId,25000);assert(manager.Find(1)->Task==&next);
manager.Release(1,next.ExecutionId,25000);assert(manager.Count(1,25000)==0); // acknowledged execution already replaced prior owner
ERR_HQOperations bounded;
for(int i=1;i<=256;i++){auto*p=new ERR_HQTask(task());assert(bounded.Queue(i,p));}
auto extra=task();assert(!bounded.Queue(257,&extra));
bounded.Clear();assert(bounded.Count(1,1000)==0&&bounded.Queue(257,&extra));
ERR_HQOperations choose;ERR_HQHypothesis h;Array<ERR_HQHypothesis*> hs{&h};auto a=force(1),c=force(2);c.Position={800,0,1000};Array<ERR_HQOperationForce*> fs{&a,&c};
assert(!choose.Choose(&b,&hs,&fs,1000,0,2500));
auto pick=choose.Choose(&b,&hs,&fs,1000,8,2500);assert(pick&&pick->SourceId==1&&pick->Task->OperationRole==ERR_HQOperationRole::OBSERVE);
assert(choose.Count(1,1000)==0); // Choosing/shadow evaluation owns no task or order.
auto replay=choose.Choose(&b,&hs,&fs,1000,8,2500);assert(replay->SourceId==pick->SourceId&&replay->Task->Destination.x==pick->Task->Destination.x&&replay->Score==pick->Score);
assert(choose.Queue(pick->SourceId,pick->Task));a.OperationId=1;a.ExecutionId=pick->Task->ExecutionId;
h.OnRoad=true;pick=choose.Choose(&b,&hs,&fs,1000,7,2500);assert(pick&&pick->SourceId==2&&pick->Task->OperationRole==ERR_HQOperationRole::BLOCK);
b.Kind=ERR_HQMemoryKind::GUNFIRE;assert(!choose.Choose(&b,&hs,&fs,1000,7,2500)); // only one uncertainty investigator
b.Kind=ERR_HQMemoryKind::SIGHTING;c.AmmoKnown=true;c.Rounds=0;pick=choose.Choose(&b,&hs,&fs,1000,7,2500);assert(pick&&pick->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
assert(pick->Task->Destination.x<c.Position.x); // regroup away from reported threat
assert(!ERR_HQOperationRules::Intercept(0,100,10,100));assert(ERR_HQOperationRules::Intercept(0,10,20,100));
assert(!ERR_HQOperationRules::Fresh(&c,120001));
// A CONTACT_CHANGED report with a small mean distance is not a formation arrival.
// The native executor derives this flag from actual original-member quorum.
{ERR_HQExecutionReport near;near.ObservedAt=1000;near.State=CONTACT_CHANGED;
near.DistanceRemaining=(10.f+50.f+50.f)/3;near.HasContact=true;
assert(!ERR_HQOperationRules::Ready(&near,2000));
near.PhysicallyArrived=true;assert(ERR_HQOperationRules::Ready(&near,2000));
assert(!ERR_HQOperationRules::Ready(&near,31001));
near.State=TRAVELLING;assert(!ERR_HQOperationRules::Ready(&near,2000));
}
// Roles advance only after both formations' physical arrival is delivered;
// suppression then grants one short bound through a delivered finite lease.
{ERR_HQOperations x;ERR_HQBelief contact;ERR_HQHypothesis route;
Array<ERR_HQHypothesis*> routes{&route};auto observer=force(31),reserve=force(32);
reserve.Position={900,0,950};auto observation=task(),holding=task();
observation.OperationRole=ERR_HQOperationRole::OBSERVE;holding.OperationRole=ERR_HQOperationRole::RESERVE;
assert(x.Queue(31,&observation)&&x.Queue(32,&holding));
observer.OperationId=reserve.OperationId=1;observer.ExecutionId=observation.ExecutionId;reserve.ExecutionId=holding.ExecutionId;
auto seen=report(observation,31,1,1,CONTACT_CHANGED,19000);seen.HasContact=true;
seen.ContactObservedAt=19000;seen.ContactPosition={1100,0,1000};seen.DistanceRemaining=0;
assert(x.Accept(&seen,19000));assert(x.ReadyCount(1,19000)==0);
seen.MessageId=2;seen.ReportSequence=2;seen.ObservedAt=20000;seen.PhysicallyArrived=true;
assert(x.Accept(&seen,20000));assert(x.ReadyCount(1,20000)==1);
Array<ERR_HQOperationForce*> observerOnly{&observer};assert(!x.Choose(&contact,&routes,&observerOnly,20000,0,2500));
auto holdingReady=report(holding,32,3,1,ARRIVED,20000);holdingReady.DistanceRemaining=0;holdingReady.PhysicallyArrived=true;
assert(x.Accept(&holdingReady,20000));
auto fixing=x.Choose(&contact,&routes,&observerOnly,21000,0,2500);
assert(fixing&&fixing->Task->OperationRole==ERR_HQOperationRole::FIX&&fixing->Task->SupportPosition.x==1100);
assert(x.Queue(31,fixing->Task));observer.ExecutionId=fixing->Task->ExecutionId;
Array<ERR_HQOperationForce*> reserveOnly{&reserve};assert(!x.Choose(&contact,&routes,&reserveOnly,21500,0,2500));
auto support=report(*fixing->Task,31,4,1,ACTING,22000);support.PhysicallyArrived=true;support.DistanceRemaining=0;
support.HasContact=true;support.ContactObservedAt=22000;support.ContactPosition={1090,0,1000};support.SupportPosition={1100,0,1000};
support.SupportReady=true;support.SupportUntil=42000;assert(x.Accept(&support,23000));
auto flanking=x.Choose(&contact,&routes,&reserveOnly,23000,0,2500);
assert(flanking&&flanking->Task->OperationRole==ERR_HQOperationRole::FLANK&&flanking->Task->Maneuver);
assert(flanking->Task->SupportSourceId==31&&flanking->Task->SupportUntil==42000&&flanking->Task->SupportPosition.x==1100);
assert(std::abs(vector::DistanceXZ(reserve.Position,flanking->Task->Destination)-20)<.01);
assert(!x.Support(1,32000)&&!x.Choose(&contact,&routes,&reserveOnly,32000,0,2500)); // lease has <=10s left
}
// An early search contact may propose a new fixing post after another distinct
// squad arrives; no old-destination arrival or searched area is manufactured.
{ERR_HQOperations x;ERR_HQBelief contact;ERR_HQHypothesis route;Array<ERR_HQHypothesis*> routes{&route};
auto search=task(),holding=task();holding.OperationRole=ERR_HQOperationRole::RESERVE;
assert(x.Queue(70,&search)&&x.Queue(71,&holding));auto seeker=force(70);
seeker.OperationId=1;seeker.ExecutionId=search.ExecutionId;Array<ERR_HQOperationForce*> units{&seeker};
auto early=report(search,70,1,1,CONTACT_CHANGED,20000);early.HasContact=true;early.ContactObservedAt=20000;
early.ContactPosition={800,0,1000};early.Position={700,0,1000};early.DistanceRemaining=400;
assert(x.Accept(&early,20000)&&!x.Find(70)->Report->PhysicallyArrived);
auto impossible=early;impossible.MessageId=90;impossible.ReportSequence=90;
impossible.ContactObservedAt=21000;assert(!x.Accept(&impossible,21000)); // contact after its report observation
impossible.ObservedAt=22000;impossible.ContactObservedAt=22000;assert(!x.Accept(&impossible,21000)); // future report
impossible.ObservedAt=21000;impossible.ContactObservedAt=std::numeric_limits<float>::quiet_NaN();assert(!x.Accept(&impossible,21000));
assert(x.Find(70)->Report->ObservedAt==20000&&x.Find(70)->Report->ContactObservedAt==20000);
assert(!x.Choose(&contact,&routes,&units,21000,0,2500)); // contact alone cannot start fixing
auto ready=report(holding,71,2,1,ARRIVED,20000);ready.DistanceRemaining=0;ready.PhysicallyArrived=true;
assert(x.Accept(&ready,20000)&&x.ReadyCount(1,21000)==1);
auto proposed=x.Choose(&contact,&routes,&units,21000,0,2500);
assert(proposed&&proposed->Task->OperationRole==ERR_HQOperationRole::FIX);
assert(proposed->Task->Destination.x==700&&proposed->Task->SupportPosition.x==800);
assert(x.Find(70)->Task==&search&&x.ReadyCount(1,21000)==1&&!x.Find(70)->Report->PhysicallyArrived&&x.Find(70)->Report->SearchedRadius==0);
auto own=x.Find(70);own->NextActionAt=22000;assert(!x.Choose(&contact,&routes,&units,21000,0,2500));own->NextActionAt=16000;
own->RetryAt=22000;assert(!x.Choose(&contact,&routes,&units,21000,0,2500));own->RetryAt=0;
seeker.ExecutionId++;assert(!x.Choose(&contact,&routes,&units,21000,0,2500));seeker.ExecutionId=search.ExecutionId;
ERR_HQBelief unrelated=contact;unrelated.BeliefId=2;assert(!x.Available(&seeker,&unrelated,21000));
assert(x.Choose(&contact,&routes,&units,30000,0,2500)&&!x.Choose(&contact,&routes,&units,30001,0,2500));
early.MessageId=3;early.ReportSequence=2;early.ObservedAt=31000;early.ContactObservedAt=20000;
assert(x.Accept(&early,31000)&&!x.Choose(&contact,&routes,&units,31000,0,2500)); // old contact despite fresh packet
early.MessageId=4;early.ReportSequence=3;early.ObservedAt=51001;early.ContactObservedAt=51001;
assert(x.Accept(&early,51001)&&!x.Choose(&contact,&routes,&units,51001,0,2500)); // other arrival stale
ready.MessageId=5;ready.ReportSequence=2;ready.ObservedAt=52000;assert(x.Accept(&ready,52000));
auto fixing=x.Choose(&contact,&routes,&units,52000,0,2500);assert(fixing&&x.Queue(70,fixing->Task));
assert(!x.Find(70)->Report&&x.Find(70)->PreviousTask==&search&&x.ReadyCount(1,52000)==1);
auto received=report(*fixing->Task,70,6,1,RECEIVED,53000);assert(x.Accept(&received,54000));
assert(x.ReadyCount(1,54000)==1&&!x.Support(1,54000)); // new FIX still needs native quorum and actual support
}
// A completed force may change beliefs, while a travelling force may not.
{ERR_HQOperations x;auto original=task();auto u=force(11);ERR_HQBelief other;other.BeliefId=2;
assert(x.Queue(11,&original));u.OperationId=1;u.ExecutionId=original.ExecutionId;
auto travelling=report(original,11,1,1,TRAVELLING,2000);assert(x.Accept(&travelling,2000));
assert(!x.Available(&u,&other,3000));
auto completed=report(original,11,2,2,COMPLETED,3000);assert(x.Accept(&completed,3000));
assert(x.Available(&u,&other,4000));
ERR_HQHypothesis route;route.BeliefId=2;Array<ERR_HQHypothesis*> routes{&route};Array<ERR_HQOperationForce*> units{&u};
assert(!x.Choose(&other,&routes,&units,4000,0,2500,0)); // no destination operation capacity
auto replacement=x.Choose(&other,&routes,&units,4000,0,2500,1);
assert(replacement&&replacement->SourceId==11&&replacement->Task->OperationId==2); // global count unchanged
assert(x.Queue(11,replacement->Task));
assert(x.Count(1,4000)==1&&x.Count(2,4000)==1); // both commitments remain until acknowledged
x.Release(11,replacement->Task->ExecutionId,5000);
assert(x.Find(11)->Task==&original&&x.Find(11)->Report->State==COMPLETED);
assert(x.Count(1,5000)==1&&x.Count(2,5000)==0);
auto acceptedTask=task(2);acceptedTask.QueuedAt=10000;acceptedTask.ExpiresAt=130000;
assert(x.Queue(11,&acceptedTask));assert(x.Count(1,10000)==1&&x.Count(2,10000)==1);
auto received=report(acceptedTask,11,3,1,RECEIVED,11000);assert(x.Accept(&received,12000));
assert(x.Count(1,12000)==0&&x.Count(2,12000)==1&&!x.Find(11)->PreviousTask&&!x.Find(11)->PreviousReport);
}
// Only an explicitly delivered matching formation closure retires commitments.
// Silence is represented by making no such call; another source is untouched.
{ERR_HQOperations x;auto owned=task(),silent=task();
assert(x.Queue(51,&owned,"formation-51")&&x.Queue(52,&silent,"formation-52"));
auto blocked=report(owned,51,20,1,BLOCKED,2000);assert(x.Accept(&blocked,3000));
auto replacement=task(2);replacement.QueuedAt=4000;assert(x.Queue(51,&replacement,"formation-51"));
auto mismatch=task(3);assert(!x.Queue(51,&mismatch,"different-formation")&&mismatch.ExecutionId==0);
assert(x.Find(51)->Task==&replacement&&x.Find(51)->PreviousTask==&owned);
x.CloseReported("");x.CloseReported("unknown");assert(x.Count(1,5000)==2&&x.Count(2,5000)==1);
int watermark=x.Find(51)->MessageId,failures=x.Find(51)->Failures;
x.CloseReported("formation-51");auto retired=x.Find(51);
assert(!retired->Task&&!retired->Report&&!retired->PreviousTask&&!retired->PreviousReport);
assert(retired->MessageId==watermark&&retired->Failures==failures&&retired->FormationSourceId=="formation-51");
assert(x.Count(1,5000)==1&&x.Count(2,5000)==0&&x.Find(52)->Task==&silent);
auto late=report(replacement,51,21,1,RECEIVED,5000);assert(!x.Accept(&late,6000));
blocked.MessageId=22;blocked.ReportSequence=2;blocked.ObservedAt=6000;assert(!x.Accept(&blocked,7000));
x.CloseReported("formation-51");assert(x.Find(52)->Task==&silent&&retired->MessageId==watermark);
}
// Global accounting is a union of current/prior orders per source; a silently
// missing local entity cannot free it, and a pending replacement counts once.
{ERR_HQOperations x;auto current=task(),prior=task(2),silent=task();
current.ExpiresAt=9000;prior.ExpiresAt=6000;silent.ExpiresAt=12000;
assert(x.CountAll(2000)==0&&!x.Active(81,2000));
assert(x.Queue(81,&current,"a")&&x.Queue(82,&silent,"silent"));
assert(x.CountAll(2000)==2&&x.Active(81,2000)&&x.Active(82,2000));
assert(x.Queue(81,&prior,"a")&&x.CountAll(3000)==2); // current+previous are one formation
assert(x.Count(1,3000)==2&&x.Count(2,3000)==1);
assert(x.Active(81,6000)&&x.CountAll(6000)==2); // prior active order outlives failed replacement deadline
assert(!x.Active(81,9000)&&x.CountAll(9000)==1); // both deadlines expired
assert(x.Active(82,11000)&&x.CountAll(11000)==1); // silence did not free its slot
x.CloseReported("silent");assert(!x.Active(82,11000)&&x.CountAll(11000)==0);
}
// Independent operation capacity includes other force families supplied by HQ.
{ERR_HQOperations x;ERR_HQBelief target;target.BeliefId=2;ERR_HQHypothesis route;route.BeliefId=2;
Array<ERR_HQHypothesis*> routes{&route};auto u=force(20);Array<ERR_HQOperationForce*> units{&u};
auto held=task(2);assert(x.Queue(99,&held));
assert(!x.Choose(&target,&routes,&units,3000,8,2500,1));
assert(x.Choose(&target,&routes,&units,3000,8,2500,2));
}
// Roster trends use the ORIGINAL status time. A newer position report,
// repeated belief surveys, and wall-clock time cannot heal squad stress.
{ERR_HQOperations x;auto u=force(101),ally=force(102);u.Position={900,0,1000};ally.Position={750,0,1000};
u.Count=ally.Count=4;u.StatusObservedAt=ally.StatusObservedAt=1000;
u.ObservedAt=ally.ObservedAt=1000;Array<ERR_HQOperationForce*> units{&u,&ally};
x.ObserveForces(&units,1000);assert(x.Judgment(101)&&!x.Judgment(101)->Recovering);
auto search=task();assert(x.Queue(101,&search));u.OperationId=1;u.ExecutionId=search.ExecutionId;
auto travelling=report(search,101,1,1,TRAVELLING,59000);assert(x.Accept(&travelling,59000));
u.StatusObservedAt=u.ObservedAt=60000;ally.StatusObservedAt=ally.ObservedAt=60000;u.Fit=3;
ERR_HQBelief target;ERR_HQHypothesis route;Array<ERR_HQHypothesis*> routes{&route};
auto regroup=x.Choose(&target,&routes,&units,60000,7,2500);auto mood=x.Judgment(101);
assert(regroup&&regroup->SourceId==101&&regroup->Task->OperationRole==ERR_HQOperationRole::RESERVE);
assert(mood->Recovering&&!mood->Withdrawing&&mood->UnfitPresent&&mood->Pressure==30);
assert(regroup->Task->Destination.x==835); // 85m short of reported rear ally, clear of its claim
assert(regroup->Task->ExpectedOutcome.find("unfit companions")!=string::npos);
float pressure=mood->Pressure,until=mood->RecoverUntil;
u.ObservedAt=ally.ObservedAt=120000;x.ObserveForces(&units,120000);
assert(mood->Pressure==pressure&&mood->StableReports==0&&mood->RecoverUntil==until);
x.ObserveForces(&units,120000);assert(mood->Pressure==pressure);
assert(x.Queue(101,regroup->Task));u.ExecutionId=regroup->Task->ExecutionId;
auto rested=report(*regroup->Task,101,2,1,COMPLETED,120000);rested.PhysicallyArrived=true;
rested.Position={780,0,1000};rested.DistanceRemaining=0;assert(x.Accept(&rested,120000));
assert(!x.Available(&u,&target,120000)); // native hold completion is not recovery
u.StatusObservedAt=120000;x.ObserveForces(&units,120000);assert(mood->Recovering&&mood->StableReports==1);
u.StatusObservedAt=u.ObservedAt=180000;ally.StatusObservedAt=ally.ObservedAt=180000;
x.ObserveForces(&units,180000);assert(mood->Recovering&&mood->Pressure==0&&mood->StableReports==2);
u.Count=u.Fit;u.StatusObservedAt=u.ObservedAt=180001;x.ObserveForces(&units,180001);
assert(!mood->Recovering&&!mood->UnfitPresent); // fresh roster resolves companion's incapacity
assert(x.Available(&u,&target,180001));
}
// A severe delivered loss withdraws once, then rests. Further planning does
// not keep retreating, and another squad closer to the threat is no refuge.
{ERR_HQOperations x;auto u=force(110),unsafe=force(111);u.Count=4;u.Position={900,0,1000};
unsafe.Position={950,0,1000};Array<ERR_HQOperationForce*> units{&u};x.ObserveForces(&units,1000);
u.Fit=2;u.Count=2;u.ObservedAt=u.StatusObservedAt=60000;
ERR_HQBelief target;ERR_HQHypothesis route;Array<ERR_HQHypothesis*> routes{&route};
auto withdraw=x.Choose(&target,&routes,&units,60000,8,2500);
assert(withdraw&&withdraw->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
assert(withdraw->Task->Destination.x==780&&x.Judgment(110)->Withdrawing);
assert(x.Queue(110,withdraw->Task));u.OperationId=1;u.ExecutionId=withdraw->Task->ExecutionId;
assert(!x.Choose(&target,&routes,&units,65000,7,2500)); // pending identity still owns commitment
auto arrived=report(*withdraw->Task,110,1,1,ARRIVED,80000);arrived.PhysicallyArrived=true;
arrived.Position={780,0,1000};arrived.DistanceRemaining=0;assert(x.Accept(&arrived,80000));
u.Position=arrived.Position;u.ObservedAt=80000;unsafe.ObservedAt=80000;units.Insert(&unsafe);
auto shelter=x.Choose(&target,&routes,&units,80000,7,2500);
assert(shelter&&shelter->SourceId==110&&shelter->Task->OperationRole==ERR_HQOperationRole::RESERVE);
assert(shelter->Task->Destination.x==780); // do not retreat twice or approach threat
assert(x.Queue(110,shelter->Task));u.ExecutionId=shelter->Task->ExecutionId;
auto waiting=report(*shelter->Task,110,2,1,ACTING,100000);waiting.PhysicallyArrived=true;
waiting.DistanceRemaining=0;assert(x.Accept(&waiting,100000));assert(!x.Available(&u,&target,100000));
u.Fit=1;u.Count=1;u.ObservedAt=u.StatusObservedAt=120000;x.ObserveForces(&units,120000);
assert(x.Available(&u,&target,120000)); // a NEW delivered loss may justify another withdrawal
auto again=x.Choose(&target,&routes,&units,120000,7,2500);
assert(again&&again->SourceId==110&&again->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
}
// A missing status never releases a reservation or invents recovery. The
// manager's detached squad state is bounded and cleared with the round.
{ERR_HQOperations x;Array<ERR_HQOperationForce*> units;
for(int i=1;i<=256;i++){auto*u=new ERR_HQOperationForce(force(i));units.Insert(u);}
x.ObserveForces(&units,1000);assert(x.Judgment(1)&&x.Judgment(256));
auto extra=force(257);Array<ERR_HQOperationForce*> overflow{&extra};x.ObserveForces(&overflow,1000);
assert(!x.Judgment(257));x.Clear();assert(!x.Judgment(1));x.ObserveForces(&overflow,1000);assert(x.Judgment(257));
}
// Failed retreat is not reported shelter; the next attempt keeps the same
// finite rally instead of adding 120m to the squad's partial progress.
{ERR_HQOperations x;auto u=force(120);u.Count=1;u.Fit=1;u.Position={900,0,1000};
ERR_HQBelief target;ERR_HQHypothesis route;Array<ERR_HQHypothesis*> routes{&route};
Array<ERR_HQOperationForce*> units{&u};auto withdrawal=x.Choose(&target,&routes,&units,1000,8,2500);
assert(withdrawal&&withdrawal->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
assert(x.Queue(120,withdrawal->Task));u.ExecutionId=withdrawal->Task->ExecutionId;u.OperationId=1;
auto blocked=report(*withdrawal->Task,120,1,1,BLOCKED,61000);blocked.Position={850,0,1000};
assert(x.Accept(&blocked,61000));u.Position=blocked.Position;u.ObservedAt=80000;u.StatusObservedAt=0;
auto retry=x.Choose(&target,&routes,&units,80000,7,2500);
assert(retry&&retry->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
assert(retry->Task->Destination.x==withdrawal->Task->Destination.x);
x.Release(120,withdrawal->Task->ExecutionId,130000);u.ExecutionId=0;u.OperationId=0;
u.ObservedAt=u.StatusObservedAt=136000;
auto afterExpiry=x.Choose(&target,&routes,&units,136000,8,2500);
assert(afterExpiry&&afterExpiry->Task->OperationRole==ERR_HQOperationRole::WITHDRAW);
assert(afterExpiry->Task->Destination.x==withdrawal->Task->Destination.x); // anchor survives task expiry
assert(x.Queue(120,afterExpiry->Task));u.ExecutionId=afterExpiry->Task->ExecutionId;u.OperationId=1;
auto arrived=report(*afterExpiry->Task,120,2,1,COMPLETED,190000);arrived.PhysicallyArrived=true;
arrived.DistanceRemaining=0;arrived.Position=afterExpiry->Task->Destination;
assert(x.Accept(&arrived,190000)&&x.Find(120)->WithdrawalArrived);
u.Position=arrived.Position;u.ObservedAt=u.StatusObservedAt=190000;
auto shelter=x.Choose(&target,&routes,&units,190000,7,2500);
assert(shelter&&shelter->Task->OperationRole==ERR_HQOperationRole::RESERVE);
assert(x.Queue(120,shelter->Task));u.ExecutionId=shelter->Task->ExecutionId;
auto rested=report(*shelter->Task,120,3,1,COMPLETED,240000);rested.PhysicallyArrived=true;
rested.DistanceRemaining=0;rested.Position=u.Position;assert(x.Accept(&rested,240000));
x.Release(120,shelter->Task->ExecutionId,310000);u.ExecutionId=0;u.OperationId=0;
u.ObservedAt=u.StatusObservedAt=316000;
auto stillShelter=x.Choose(&target,&routes,&units,316000,8,2500);
assert(stillShelter&&stillShelter->Task->OperationRole==ERR_HQOperationRole::RESERVE);
assert(stillShelter->Task->Destination.x==withdrawal->Task->Destination.x); // no endless120m retreats
}
// A no-contact packet does not erase a recently delivered firefight. Quiet
// recovery requires actual elapsed time since that original observation.
{ERR_HQOperations x;auto u=force(130);u.Fit=1;u.Count=1;u.AmmoKnown=true;u.Rounds=0;
Array<ERR_HQOperationForce*> units{&u};x.ObserveForces(&units,0);auto duty=task();assert(x.Queue(130,&duty));
auto contact=report(duty,130,1,1,CONTACT_CHANGED,50000);contact.HasContact=true;
contact.ContactObservedAt=50000;contact.ContactPosition={1100,0,1000};assert(x.Accept(&contact,50000));
auto quiet=report(duty,130,2,2,ACTING,59000);assert(x.Accept(&quiet,59000));
u.ObservedAt=u.StatusObservedAt=60000;u.Fit=4;u.Count=4;u.Rounds=400;x.ObserveForces(&units,60000);
assert(x.Judgment(130)->StableReports==0&&x.Judgment(130)->Recovering);
u.ObservedAt=u.StatusObservedAt=120000;x.ObserveForces(&units,120000);assert(x.Judgment(130)->StableReports==1);
u.ObservedAt=u.StatusObservedAt=180000;x.ObserveForces(&units,180000);assert(!x.Judgment(130)->Recovering);
}
// A previously queued quiet roster cannot clear recovery after newer
// delivered contact; it also cannot explain ammo expenditure before itself.
{ERR_HQOperations x;auto u=force(140);u.Fit=1;u.Count=1;u.AmmoKnown=true;u.Rounds=0;
Array<ERR_HQOperationForce*> units{&u};x.ObserveForces(&units,0);
u.Fit=4;u.Count=4;u.Rounds=400;u.ObservedAt=u.StatusObservedAt=60000;
x.ObserveForces(&units,60000);assert(x.Judgment(140)->StableReports==1);
auto duty=task();duty.ExpiresAt=300000;assert(x.Queue(140,&duty));
auto contact=report(duty,140,1,1,CONTACT_CHANGED,130000);contact.HasContact=true;
contact.ContactObservedAt=130000;contact.ContactPosition={1100,0,1000};assert(x.Accept(&contact,130000));
u.ObservedAt=130000;u.StatusObservedAt=120000;u.Rounds=200;x.ObserveForces(&units,130000);
assert(x.Judgment(140)->Recovering&&x.Judgment(140)->StableReports==0&&x.Judgment(140)->Pressure==0);
}
// Delivery delay does not make an older quiet roster post-contact evidence.
// Even a contact now older than60s still chronologically follows this roster.
{ERR_HQOperations x;auto u=force(143);u.Fit=1;u.Count=1;u.AmmoKnown=true;u.Rounds=0;
Array<ERR_HQOperationForce*> units{&u};x.ObserveForces(&units,0);
u.Fit=4;u.Count=4;u.Rounds=400;u.ObservedAt=u.StatusObservedAt=60000;
x.ObserveForces(&units,60000);assert(x.Judgment(143)->StableReports==1);
auto duty=task();duty.ExpiresAt=400000;assert(x.Queue(143,&duty));
auto contact=report(duty,143,1,1,CONTACT_CHANGED,145000);contact.HasContact=true;
contact.ContactObservedAt=145000;contact.ContactPosition={1100,0,1000};assert(x.Accept(&contact,145000));
u.ObservedAt=145000;u.StatusObservedAt=120000;u.Rounds=200;x.ObserveForces(&units,210000);
assert(x.Judgment(143)->Recovering&&x.Judgment(143)->StableReports==0&&x.Judgment(143)->Pressure==0);
u.ObservedAt=u.StatusObservedAt=220000;x.ObserveForces(&units,220000);
assert(x.Judgment(143)->Recovering&&x.Judgment(143)->StableReports==1);
u.ObservedAt=u.StatusObservedAt=280000;x.ObserveForces(&units,280000);
assert(!x.Judgment(143)->Recovering); // genuine post-contact quiet reports permit recovery
}
}
''')
for forbidden in ('GetPlayers(', 'GetPlayerManager(', 'GetOrigin(', 'GetGame(', 'IEntity', 'SCR_AIGroup', 'Spawn('):
assert forbidden not in source
print('PASS: actual operation selection/replay, pending reservations, detached reports, identity/sequence/time gates, terminal lifecycle, bounded ledger, finite roles, no shadow mutation or hidden-player inputs')