243 lines
20 KiB
Python
243 lines
20 KiB
Python
"""Execute production refresh/adoption methods against adversarial radio/solver outcomes.
|
|
|
|
The real scheduler, refresh, cancellation and projectile-bookkeeping bodies run.
|
|
Native route/ballistic/crew-readiness operations, battery Update and initial-order
|
|
progression are boundary adapters; this is not native delivery, death or firing.
|
|
"""
|
|
import re
|
|
from policy_harness import ROOT, body, run
|
|
r=ROOT/'addons/ai/Scripts/Game/ERR'
|
|
|
|
def cpp(path, signature):
|
|
code=body(r/path,signature).replace('protected ','')
|
|
for typ in ['ERR_MortarBattery','ERR_MortarSystem','ERR_HQReport','ERR_HQRoute','ERR_MortarRefresh','ERR_MortarAimSolution']:
|
|
code=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',code)
|
|
for name in ['battery','report','current','incoming','update','route','commanded','updated','system','m_HQ','newest','selected','target','m_Transfer','Activity']:
|
|
code=code.replace(name+'.',name+'->')
|
|
# Members are owning/weak script references, both represented as pointers.
|
|
for name in ['Route','Report','Refresh','AimSolver']:
|
|
code=code.replace('.'+name+'.','.'+name+'->').replace('->'+name+'.','->'+name+'->')
|
|
code=code.replace('Route.Advance','Route->Advance')
|
|
for name in ['ERR_MortarPrediction','ERR_MortarRefreshRules','ERR_MortarAimRules','ERR_MortarBattery']:
|
|
code=code.replace(name+'.',name+'::')
|
|
code=code.replace('null','nullptr').replace('IEntity gun;','IEntity gun=nullptr;').replace('ResourceName ammo;','std::string ammo;')
|
|
code=code.replace('string reason','std::string reason')
|
|
# Only journal formatting is inert; authorization/solver mutations are real bodies.
|
|
code=re.sub(r'^.*Audit\([^\n]*$', ';', code, flags=re.M)
|
|
code=code.replace('vector.Distance','vector::Distance').replace('Math.AbsFloat','std::fabs')
|
|
code=code.replace('array<ref ERR_HQReport> reports = {};','Items<ERR_HQReport*> reports;')
|
|
code=re.sub(r'foreach \((ERR_MortarBattery|ERR_HQReport)\* (\w+) : (\w+)\)',r'for (\1* \2 : \3.values)',code)
|
|
code=re.sub(r'(ERR_MortarBattery|ERR_HQReport)\* (newest|selected|target);',r'\1* \2=nullptr;',code)
|
|
code=code.replace('Replication.IsServer()', 'Replication::IsServer()').replace('float.MAX','1e30f')
|
|
return code
|
|
|
|
constructor=cpp('ERR_MortarRefresh.c','void ERR_MortarRefresh(').replace('void ERR_MortarRefresh(', 'ERR_MortarRefresh(')
|
|
newer=cpp('ERR_MortarRefresh.c','static bool Newer(')
|
|
owns=cpp('ERR_MortarRefresh.c','bool Owns(')
|
|
delivered=cpp('ERR_MortarRefresh.c','bool DeliveredFor(')
|
|
fresh=cpp('ERR_MortarPrediction.c','static bool Fresh(')
|
|
equivalent=cpp('ERR_MortarAimSolution.c','static bool Equivalent(')
|
|
matches=cpp('ERR_MortarAimSolution.c','bool Matches(')
|
|
can=cpp('ERR_MortarBattery.c','bool CanRefresh(')
|
|
begin=cpp('ERR_MortarBattery.c','bool BeginRefresh(')
|
|
reject=cpp('ERR_MortarBattery.c','void RejectRefresh(')
|
|
advance=cpp('ERR_MortarSystem.c','protected void AdvanceRefresh(')
|
|
adopt=cpp('ERR_MortarFiring.c','bool ERR_AdoptReport(')
|
|
cancel=cpp('ERR_MortarBattery.c','void Cancel(')
|
|
projectile=cpp('ERR_MortarBattery.c','void ObserveProjectile(')
|
|
queue_method=cpp('ERR_MortarSystem.c','protected void QueueRefreshes(')
|
|
tick=cpp('ERR_MortarSystem.c','void Tick(')
|
|
# Supply owners before methods that dereference them, without replacing method logic.
|
|
can=can.replace('bool CanRefresh(', 'bool ERR_MortarBattery::CanRefresh(')
|
|
begin=begin.replace('bool BeginRefresh(', 'bool ERR_MortarBattery::BeginRefresh(')
|
|
reject=reject.replace('void RejectRefresh(', 'void ERR_MortarBattery::RejectRefresh(')
|
|
advance=advance.replace('void AdvanceRefresh(', 'void ERR_MortarSystem::AdvanceRefresh(')
|
|
adopt=adopt.replace('bool ERR_AdoptReport(', 'bool Node::ERR_AdoptReport(')
|
|
cancel=cancel.replace('void Cancel(', 'void ERR_MortarBattery::Cancel(').replace('std::string reason = "cancel requested"','std::string reason')
|
|
projectile=projectile.replace('void ObserveProjectile(', 'void ERR_MortarBattery::ObserveProjectile(')
|
|
queue_method=queue_method.replace('void QueueRefreshes(', 'void ERR_MortarSystem::QueueRefreshes(')
|
|
tick=tick.replace('void Tick(', 'void ERR_MortarSystem::Tick(')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
struct vector {float x=0,y=0,z=0;static float Distance(vector a,vector b){return std::sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}static float DistanceXZ(vector a,vector b){a.y=0;b.y=0;return Distance(a,b);}};
|
|
using IEntity=void*;
|
|
template<class T>struct Items {std::vector<T> values;void Insert(T t){values.push_back(t);}};
|
|
struct Replication {static inline bool server=true;static bool IsServer(){return server;}};
|
|
struct ERR_HQReport {IEntity Target=this;int MessageId=1;float ObservedAt=0;bool Distress=false;vector Position{};std::string TravelMode="foot";std::string Description(float){return "contact";}ERR_HQReport* Snapshot(){return new ERR_HQReport(*this);}};
|
|
struct ERR_MortarPrediction {static constexpr float MAX_REPORT_AGE_MS=60000;
|
|
'''+fresh+r'''};
|
|
struct ERR_MortarRefreshRules {
|
|
'''+newer+r'''};
|
|
struct ERR_MortarAimRules {
|
|
'''+equivalent+r'''};
|
|
struct ERR_MortarBattery;struct ERR_MortarSystem;struct ERR_MortarRefresh;struct ERR_MortarAimSolution;struct Node;
|
|
struct ERR_HQRoute {int MessageId=9,state=0,calls=0;int Advance(float,int){calls++;return state;}};
|
|
struct HQ {Items<ERR_HQReport*> contacts;ERR_HQRoute* nextRoute=nullptr;int downlinks=0;int Journal(){return 0;}void SupportContacts(Items<ERR_HQReport*>& out,float){out=contacts;}ERR_HQRoute* Downlink(IEntity,const char*,std::string,int){downlinks++;return nextRoute;}};
|
|
struct Ledger {std::set<int> claimed;bool Available(int id,float,float){return !claimed.count(id);}bool Claim(int id,float,float){return claimed.insert(id).second;}};
|
|
struct FakeActivity {int completions=0;void Complete(){completions++;}};
|
|
struct Transfer {int invalidated=0;void Invalidate(){invalidated++;}};
|
|
struct ERR_MortarBattery {
|
|
static inline std::string SHELL="HE";
|
|
IEntity Gun=this,Group=this;int OrderId=1,OrderShots=0,TotalShots=0,RadioId=0;float OrderedAt=500,AimedAt=1000,AimValidatedAt=1000,NextOrder=0;vector Position{};
|
|
bool m_Stopped=false,CommandOpen=true,RefreshRejected=false,Rejected=false,Released=false,operational=true,StartupActivated=true;
|
|
FakeActivity originalActivity;FakeActivity* Activity=&originalActivity;Transfer transfer;Transfer* m_Transfer=&transfer;
|
|
ERR_HQRoute* RadioOrder=nullptr;ERR_HQReport* PendingRadioReport=nullptr;ERR_HQReport* PendingSupplyReport=nullptr;
|
|
std::string LastFailure;ERR_HQReport* Report=nullptr;ERR_MortarRefresh* Refresh=nullptr;ERR_MortarAimSolution* AimSolution=nullptr;Node* AimSolver=nullptr;Ledger m_ReportLedger;
|
|
bool Operational(){return operational;}
|
|
bool CanRefresh(ERR_HQReport*,float);bool BeginRefresh(ERR_HQReport*,ERR_HQRoute*,float);void RejectRefresh(std::string);
|
|
void Cancel(std::string reason="cancel requested");void ObserveProjectile(IEntity,IEntity,bool);void FlushTraces(){}void DetachActivity(){}
|
|
void Update(float){}bool SupplyPending(){return false;}int CountShells(){return 27;}
|
|
bool CanRequest(ERR_HQReport*,float){return false;}bool BeginRequest(ERR_HQReport*,float){assert(false);return false;}
|
|
};
|
|
struct ERR_MortarRefresh {
|
|
IEntity Gun=nullptr,Group=nullptr;int OrderId=0,RadioId=0;ERR_HQReport* Report=nullptr;ERR_HQRoute* Route=nullptr;
|
|
'''+constructor+owns+delivered+r'''};
|
|
struct ERR_MortarAimSolution {
|
|
IEntity Gun=nullptr;ERR_HQReport* Report=nullptr;int OrderId=1,Shot=0,Charge=0;vector GunOrigin{},Impact{500,0,0},AimTarget{500,500,0};float FlightTime=30;
|
|
'''+matches+r'''};
|
|
struct ERR_MortarSystem {
|
|
HQ hq;HQ* m_HQ=&hq;int failures=0;
|
|
Items<ERR_MortarBattery*> m_Batteries;bool m_Stopped=false;float m_NextDecision=1e9f,m_NextRefreshDecision=0;
|
|
bool Withhold(ERR_MortarBattery* b,const char* why){b->Rejected=true;b->LastFailure=why;failures++;return false;}
|
|
void AdvanceRefresh(ERR_MortarBattery*,float);
|
|
void QueueRefreshes(float);void Tick(float);void AdvanceOrder(ERR_MortarBattery*,float){}bool OutsideSettlements(vector){return true;}
|
|
};
|
|
struct Node {
|
|
static constexpr int PORT_ARTILLERY_ENTITY=1,PORT_AMMO_PREFAB=2;
|
|
IEntity portGun=nullptr;std::string portAmmo=ERR_MortarBattery::SHELL;ERR_MortarAimSolution* calculated=nullptr;int solves=0;
|
|
void GetVariableIn(int,IEntity& out){out=portGun;}void GetVariableIn(int,std::string& out){out=portAmmo;}
|
|
ERR_MortarAimSolution* ERR_Solve(ERR_MortarBattery*,ERR_MortarSystem*,ERR_HQReport*,float){solves++;return calculated;}
|
|
bool ERR_AdoptReport(ERR_MortarBattery*,ERR_MortarSystem*,ERR_HQReport*,float);
|
|
};
|
|
'''+can+begin+reject+adopt+advance+cancel+projectile+queue_method+tick+r'''
|
|
struct F {
|
|
ERR_MortarBattery b;ERR_HQReport old,next;ERR_HQRoute route;ERR_MortarSystem system;ERR_MortarAimSolution original,solved;Node node;
|
|
F(){next=old;next.MessageId=2;next.ObservedAt=20000;b.Report=&old;original.Gun=b.Gun;original.Report=&old;solved=original;node.portGun=b.Gun;node.calculated=&solved;b.AimSolver=&node;system.m_Batteries.Insert(&b);}
|
|
bool queue(){return b.BeginRefresh(&next,&route,25000);}
|
|
void aim(){b.AimSolution=&original;}
|
|
void delivery(float now=26000){route.state=1;system.AdvanceRefresh(&b,now);}
|
|
};
|
|
int main(){
|
|
F good;assert(good.queue());assert(!good.queue());assert(good.b.m_ReportLedger.claimed.count(2));
|
|
good.system.AdvanceRefresh(&good.b,25000);assert(good.b.Report==&good.old&&good.route.calls==1); // no instant HQ knowledge
|
|
good.delivery();assert(!good.b.Refresh&&!good.b.RefreshRejected&&good.b.Report->MessageId==2);
|
|
assert(good.b.Report->ObservedAt==20000&&good.b.OrderId==1&&good.b.OrderShots==0&&good.b.OrderedAt==500&&good.b.Activity);
|
|
assert(!good.queue());good.b.Report=&good.old;assert(!good.queue()); // claimed refresh cannot become replay after original restored
|
|
for(int fault=0;fault<10;fault++){
|
|
F f;
|
|
if(fault==0)f.next.Target=&f;
|
|
if(fault==1)f.next.ObservedAt=0;
|
|
if(fault==2)f.next.ObservedAt=-1;
|
|
if(fault==3)f.next.ObservedAt=25001;
|
|
if(fault==4)f.next.MessageId=1;
|
|
if(fault==5)f.next.MessageId=0;
|
|
if(fault==6)f.next.Distress=true;
|
|
if(fault==7)f.next.TravelMode="aircraft";
|
|
if(fault==8)f.b.CommandOpen=false;
|
|
if(fault==9)f.b.operational=false;
|
|
assert(!f.queue()&&f.b.m_ReportLedger.claimed.empty());
|
|
}
|
|
F absent;assert(!absent.b.BeginRefresh(&absent.next,nullptr,25000)&&absent.b.m_ReportLedger.claimed.empty());
|
|
F expired;assert(expired.queue());expired.delivery(80001);assert(expired.b.RefreshRejected&&expired.b.Report==&expired.old);
|
|
F failed;assert(failed.queue());failed.route.state=-1;failed.system.AdvanceRefresh(&failed.b,26000);assert(failed.b.RefreshRejected&&!failed.b.Refresh&&failed.b.Report==&failed.old);
|
|
F changed;assert(changed.queue());changed.b.Gun=&changed.old;changed.delivery();assert(changed.b.RefreshRejected);
|
|
F group;assert(group.queue());group.b.Group=&group.old;group.delivery();assert(group.b.RefreshRejected);
|
|
F stale;assert(stale.queue());stale.b.OrderId++;stale.delivery();assert(!stale.b.Refresh&&!stale.b.RefreshRejected&&stale.b.Report==&stale.old&&stale.route.calls==0);
|
|
F closed;assert(closed.queue());closed.b.CommandOpen=false;closed.delivery();assert(!closed.b.Refresh&&!closed.b.RefreshRejected&&closed.route.calls==0);
|
|
F committed;assert(committed.queue());committed.b.Released=true;committed.delivery();
|
|
assert(committed.b.Refresh&&!committed.b.Refresh->Route&&committed.b.Report==&committed.old&&committed.b.Released);
|
|
committed.b.Released=false;committed.b.OrderShots=1;committed.system.AdvanceRefresh(&committed.b,27000);
|
|
assert(!committed.b.Refresh&&committed.b.Report->MessageId==2&&committed.b.OrderShots==1&&committed.b.OrderId==1);
|
|
F failInFlight;assert(failInFlight.queue());failInFlight.b.Released=true;failInFlight.route.state=-1;failInFlight.system.AdvanceRefresh(&failInFlight.b,26000);
|
|
assert(failInFlight.b.RefreshRejected&&failInFlight.b.Released&&failInFlight.b.CommandOpen&&failInFlight.b.Report==&failInFlight.old); // callback ownership retained; future load gate closes
|
|
F validAim;validAim.aim();assert(validAim.queue());validAim.delivery();
|
|
assert(validAim.node.solves==1&&!validAim.b.RefreshRejected&&validAim.b.Report->MessageId==2&&validAim.original.Report==validAim.b.Report);
|
|
assert(validAim.b.AimedAt==1000&&validAim.b.AimValidatedAt==26000&&validAim.b.AimSolution==&validAim.original&&validAim.original.Impact.x==500);
|
|
for(int fault=0;fault<10;fault++){
|
|
F f;f.aim();assert(f.queue());
|
|
if(fault==0)f.solved.Impact.x+=.101f;
|
|
if(fault==1)f.solved.Charge++;
|
|
if(fault==2)f.solved.AimTarget.y+=.101f;
|
|
if(fault==3)f.solved.GunOrigin.y+=.051f;
|
|
if(fault==4)f.solved.FlightTime+=.051f;
|
|
if(fault==5)f.node.portGun=&f.old;
|
|
if(fault==6)f.node.portAmmo="other";
|
|
if(fault==7)f.b.AimSolver=nullptr;
|
|
if(fault==8)f.node.calculated=nullptr;
|
|
if(fault==9)f.b.AimedAt=27000;
|
|
f.delivery();assert(f.b.RefreshRejected&&f.b.Report==&f.old&&f.original.Report==&f.old&&f.b.AimValidatedAt==1000);
|
|
}
|
|
F notDelivered;notDelivered.aim();assert(notDelivered.queue());assert(!notDelivered.node.ERR_AdoptReport(¬Delivered.b,¬Delivered.system,notDelivered.b.Refresh->Report,26000));assert(notDelivered.node.solves==0);
|
|
F creep;creep.aim();creep.solved.Impact.x+=.06f;assert(creep.queue());creep.delivery();assert(!creep.b.RefreshRejected);
|
|
creep.next.MessageId=3;creep.next.ObservedAt=22000;creep.solved.Impact.x+=.06f;assert(creep.queue());creep.delivery(27000);
|
|
assert(creep.b.RefreshRejected&&creep.original.Impact.x==500&&creep.b.Report->MessageId==2&&creep.b.AimedAt==1000);
|
|
// Execute the actual HQ tick, refresh scheduler, cancellation and projectile
|
|
// bookkeeping together. Native route/crew/ballistic operations remain stubs.
|
|
F repeated;repeated.system.hq.contacts.Insert(&repeated.next);repeated.system.hq.nextRoute=&repeated.route;
|
|
repeated.system.Tick(25000);assert(repeated.system.hq.downlinks==1&&repeated.b.Refresh&&repeated.route.calls==0);
|
|
repeated.system.Tick(25001);repeated.system.Tick(30000);assert(repeated.system.hq.downlinks==1&&repeated.route.calls==2);
|
|
repeated.route.state=1;repeated.system.Tick(30001);assert(!repeated.b.Refresh&&repeated.b.Report->MessageId==2);
|
|
repeated.system.Tick(35000);repeated.system.hq.contacts.Insert(&repeated.old);repeated.system.Tick(40000);
|
|
assert(repeated.system.hq.downlinks==1&&repeated.b.OrderId==1&&repeated.b.Activity==&repeated.b.originalActivity);
|
|
assert(repeated.b.m_ReportLedger.claimed.size()==1&&repeated.b.Report->ObservedAt==20000);
|
|
F snapshot;assert(snapshot.queue());snapshot.next.MessageId=99;snapshot.next.ObservedAt=24999;
|
|
snapshot.delivery();assert(snapshot.b.Report->MessageId==2&&snapshot.b.Report->ObservedAt==20000); // in-flight packet is immutable
|
|
F newest;ERR_HQReport later=newest.next;later.MessageId=3;later.ObservedAt=22000;
|
|
newest.system.hq.contacts.Insert(&newest.next);newest.system.hq.contacts.Insert(&later);newest.system.hq.nextRoute=&newest.route;
|
|
newest.system.Tick(25000);assert(newest.b.Refresh->Report->MessageId==3&&newest.b.m_ReportLedger.claimed.count(3)&&!newest.b.m_ReportLedger.claimed.count(2));
|
|
F interrupted;assert(interrupted.queue());interrupted.b.Released=true;
|
|
interrupted.system.Tick(25000);interrupted.route.state=-1;interrupted.system.Tick(25001);
|
|
assert(interrupted.b.RefreshRejected&&interrupted.b.Released&&interrupted.b.CommandOpen&&interrupted.b.Report==&interrupted.old);
|
|
interrupted.system.Tick(25002);assert(interrupted.b.RefreshRejected&&interrupted.b.Released&&interrupted.route.calls==2);
|
|
interrupted.b.ObserveProjectile(&interrupted.next,nullptr,false); // actual physical-event bookkeeping, not native launch proof
|
|
assert(!interrupted.b.Released&&interrupted.b.OrderShots==1&&interrupted.b.TotalShots==1&&interrupted.b.Report==&interrupted.old);
|
|
interrupted.system.Tick(25003);assert(!interrupted.b.CommandOpen&&!interrupted.b.Activity&&!interrupted.b.Report&&!interrupted.b.RefreshRejected);
|
|
assert(interrupted.b.originalActivity.completions==1&&interrupted.b.transfer.invalidated==1&&interrupted.b.NextOrder==35003);
|
|
assert(interrupted.b.OrderShots==1&&interrupted.b.TotalShots==1&&interrupted.b.m_ReportLedger.claimed.count(2));
|
|
F noRoute;noRoute.system.hq.contacts.Insert(&noRoute.next);noRoute.b.Released=true;
|
|
noRoute.system.Tick(25000);assert(noRoute.system.hq.downlinks==1&&noRoute.b.RefreshRejected&&noRoute.b.Released);
|
|
noRoute.system.Tick(30000);assert(noRoute.system.hq.downlinks==1&&noRoute.b.CommandOpen); // latch cannot reset while committed
|
|
F deliveredThenRadioDies;assert(deliveredThenRadioDies.queue());deliveredThenRadioDies.b.Released=true;deliveredThenRadioDies.route.state=1;
|
|
deliveredThenRadioDies.system.Tick(26000);assert(deliveredThenRadioDies.b.Refresh&&!deliveredThenRadioDies.b.Refresh->Route&&deliveredThenRadioDies.route.calls==1);
|
|
deliveredThenRadioDies.route.state=-1; // loss AFTER complete delivery cannot erase the packet already received
|
|
deliveredThenRadioDies.b.ObserveProjectile(&deliveredThenRadioDies.next,nullptr,false);
|
|
deliveredThenRadioDies.system.Tick(27000);assert(!deliveredThenRadioDies.b.RefreshRejected&&deliveredThenRadioDies.b.Report->MessageId==2&&deliveredThenRadioDies.route.calls==1);
|
|
assert(deliveredThenRadioDies.b.AimedAt==-1&&deliveredThenRadioDies.b.AimValidatedAt==-1&&deliveredThenRadioDies.b.OrderShots==1);
|
|
F expiresInFlight;assert(expiresInFlight.queue());expiresInFlight.b.Released=true;expiresInFlight.route.state=1;
|
|
expiresInFlight.system.Tick(26000);expiresInFlight.system.Tick(80001);
|
|
assert(expiresInFlight.b.RefreshRejected&&expiresInFlight.b.Released&&expiresInFlight.b.Report==&expiresInFlight.old);
|
|
expiresInFlight.b.ObserveProjectile(&expiresInFlight.next,nullptr,false);expiresInFlight.system.Tick(80002);
|
|
assert(!expiresInFlight.b.CommandOpen&&expiresInFlight.b.TotalShots==1&&expiresInFlight.b.OrderShots==1);
|
|
F crewLost;assert(crewLost.queue());crewLost.b.operational=false;crewLost.delivery();
|
|
assert(crewLost.b.RefreshRejected&&crewLost.route.calls==0&&crewLost.b.Report==&crewLost.old); // readiness loss refuses before delivery
|
|
F cancelled;assert(cancelled.queue());ERR_MortarRefresh* cancelledPacket=cancelled.b.Refresh;
|
|
cancelled.b.Cancel("explicit cancellation");cancelled.route.state=1;cancelled.system.Tick(26000);
|
|
assert(!cancelled.b.Refresh&&!cancelled.b.Report&&cancelled.route.calls==0&&cancelled.b.m_ReportLedger.claimed.count(2));
|
|
cancelled.b.OrderId=2;cancelled.b.CommandOpen=true;cancelled.b.Activity=&cancelled.b.originalActivity;cancelled.b.Report=&cancelled.next;
|
|
cancelled.b.Refresh=cancelledPacket;cancelled.system.AdvanceRefresh(&cancelled.b,27000); // delayed old-order transaction cannot hit replacement
|
|
assert(!cancelled.b.Refresh&&!cancelled.b.RefreshRejected&&cancelled.b.CommandOpen&&cancelled.b.OrderId==2&&cancelled.b.Report==&cancelled.next&&cancelled.route.calls==0);
|
|
F stopped;assert(stopped.queue());stopped.system.m_Stopped=true;stopped.system.Tick(26000);assert(stopped.route.calls==0&&stopped.b.Refresh);
|
|
stopped.system.m_Stopped=false;Replication::server=false;stopped.system.Tick(26000);assert(stopped.route.calls==0);Replication::server=true;
|
|
}
|
|
''')
|
|
# Boundaries not modeled as native C++ operations: real HQ route construction,
|
|
# projectile latch, immutable report timestamps, existing cadence and cleanup.
|
|
system=(r/'ERR_MortarSystem.c').read_text(); battery=(r/'ERR_MortarBattery.c').read_text()
|
|
queue=body(r/'ERR_MortarSystem.c','protected void QueueRefreshes(')
|
|
assert 'm_HQ.SupportContacts(reports, now)' in queue and 'm_HQ.Downlink(' in queue
|
|
assert 'new SCR_AIStaticArtilleryActivity' not in queue+begin+advance+adopt
|
|
assert 'ObservedAt =' not in queue+begin+advance+adopt
|
|
assert 'OrderId++' not in queue+begin+advance+adopt
|
|
assert 'OrderedAt =' not in queue+begin+advance+adopt
|
|
assert 'battery.RefreshRejected' in body(r/'ERR_MortarSystem.c','bool AllowShot(')
|
|
assert '(battery.Rejected || battery.RefreshRejected) && !battery.Released' in system
|
|
assert 'Refresh = null;' in body(r/'ERR_MortarBattery.c','void Cancel(')
|
|
assert 'AimValidatedAt = -1;' in body(r/'ERR_MortarBattery.c','void ObserveProjectile(')
|
|
assert 'battery.AimedAt = now;' not in body(r/'ERR_MortarFiring.c','bool ERR_AdoptReport(')
|
|
assert 'battery.AimSolution = updated' not in adopt
|
|
print('PASS: actual refresh scheduler/delivery/cancellation/projectile methods reject interrupted or replayed reports, retain committed ownership, preserve immutable snapshots/aim geometry and isolate stopped or replaced orders; native radio/crew behavior remains separate')
|