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:
@@ -4,16 +4,17 @@ import os,re
|
||||
from policy_harness import ROOT,body,run
|
||||
BASE=Path(os.environ.get('ERR_AMMO_REPORTING_ROOT',ROOT))
|
||||
P=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoReporting.c'
|
||||
H=BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'
|
||||
A=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c'
|
||||
H=Path(os.environ.get('ERR_HQ_SOURCE',BASE/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
|
||||
A=Path(os.environ.get('ERR_AMMO_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQAmmoSampler.c'))
|
||||
M=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c'
|
||||
|
||||
def cpp(s):
|
||||
s=s.replace('ERR_HQLocalAwareness.Priority','ERR_HQLocalAwareness::Priority').replace('protected ','').replace('ERR_HQPursuitRules.', 'ERR_HQPursuitRules::').replace('string.Format','Format')
|
||||
for t in ('ERR_HQStatusReport','ERR_HQAmmoSnapshot','ERR_HQUnit','ERR_HQRoute'):
|
||||
for t in ('ERR_HQStatusReport','ERR_HQAmmoSnapshot','ERR_HQMountedAmmoSnapshot','ERR_HQUnit','ERR_HQRoute'):
|
||||
s=re.sub(r'\b'+t+r' (?=\w)',t+'* ',s)
|
||||
for v in ('stock','report','unit','route'):
|
||||
for v in ('stock','mounted','report','unit','route'):
|
||||
s=s.replace(v+'.',v+'->')
|
||||
return s.replace('AmmoState.','AmmoState->').replace('Task.','Task->')
|
||||
return s.replace('MountedAmmo.','MountedAmmo->').replace('AmmoState.','AmmoState->').replace('Task.','Task->')
|
||||
policy=cpp(P.read_text()).replace('class ERR_HQAmmoReporting','struct ERR_HQAmmoReporting').rstrip()+';'
|
||||
methods='\n'.join(cpp(body(H,x)) for x in ('protected void StatusReport(', 'protected void QueueReports('))
|
||||
# The production hook must preserve the only existing delivery gate and use the policy per identity.
|
||||
@@ -31,25 +32,29 @@ using string=std::string;
|
||||
template<class... T>string Format(T...){return "sample";}
|
||||
struct ERR_HQPursuitRules{static bool Finite(float v){return std::isfinite(v)&&v>-1e20&&v<1e20;}};
|
||||
struct ERR_HQAmmoSnapshot{
|
||||
bool MineComplete=false;int MineMembers=0,ReadyMines=0;
|
||||
bool Complete=true;int Members=4,SmallArmsMuzzles=4,SmallArmsRounds=121,SmallArmsMagazines=4,SmallArmsChambers=0;
|
||||
int LauncherRounds=0,LauncherMagazines=0,LauncherChambers=0;
|
||||
int LauncherMuzzles=0,LauncherRounds=0,LauncherMagazines=0,LauncherChambers=0;
|
||||
'''+body(A,' bool Valid(')+r'''
|
||||
};
|
||||
struct ERR_HQStatusReport{ERR_HQAmmoSnapshot* AmmoState=nullptr;int MessageId=0,Count=4,Fit=4,ConfirmedLosses=0;string SourceId="formation-1";float ObservedAt=0;};
|
||||
struct ERR_HQMountedAmmoSnapshot{bool Complete=true;int Weapons=1,Muzzles=1,Magazines=1,Rounds=100,Capacity=100,Chambers=0,LowMagazines=0;
|
||||
'''+body(M,' 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="formation-1";float ObservedAt=0;};
|
||||
'''+policy+r'''
|
||||
struct Sampler{int observations=0,samples=0;bool valid=true;ERR_HQAmmoSnapshot stock;
|
||||
struct Sampler{int observations=0,samples=0;bool valid=true;ERR_HQAmmoSnapshot stock;ERR_HQMountedAmmoSnapshot mounted;
|
||||
void ObserveDeaths(){observations++;}
|
||||
ERR_HQStatusReport* Sample(float now,int id,void*){samples++;if(!valid)return nullptr;
|
||||
auto r=new ERR_HQStatusReport;r->ObservedAt=now;r->MessageId=id;r->AmmoState=new ERR_HQAmmoSnapshot(stock);return r;}};
|
||||
auto r=new ERR_HQStatusReport;r->ObservedAt=now;r->MessageId=id;r->AmmoState=new ERR_HQAmmoSnapshot(stock);r->MountedAmmo=new ERR_HQMountedAmmoSnapshot(mounted);return r;}};
|
||||
struct ERR_HQRoute{int MessageId=0;string Payload;};
|
||||
struct Network{bool available=true;int routes=0;ERR_HQRoute* Route(void*){routes++;return available?new ERR_HQRoute:nullptr;}};
|
||||
struct Journal{int seq=0,writes=0;int Message(){return ++seq;}void Write(string,string,int){writes++;}};
|
||||
struct Task{float ArrivedAt=0;};
|
||||
struct ERR_HQLocalAwareness{static bool Priority(float,float,float){return false;}};
|
||||
struct ERR_HQUnit{float NextContact=0;struct Task* Task=nullptr;void* Radio=this;ERR_HQRoute* Uplink=nullptr;ERR_HQStatusReport* PendingStatus=nullptr;
|
||||
struct ERR_HQUnit{bool ExecutionTurn=true;float NextContact=0;struct Task* Task=nullptr;void* Radio=this;ERR_HQRoute* Uplink=nullptr;ERR_HQStatusReport* PendingStatus=nullptr;
|
||||
float NextStatus=0;ERR_HQAmmoReporting AmmoReporting;Sampler StatusSampler;bool visible=true,contact=false,sweep=false;
|
||||
ERR_HQUnit(){AmmoReporting.Initialize(6,0);}};
|
||||
struct HQ{void ContactReport(ERR_HQUnit*,float){}
|
||||
struct HQ{bool m_Coordinated=false;void QueueExecution(ERR_HQUnit*,float){}void ContactReport(ERR_HQUnit*,float){}
|
||||
Network m_Network;Journal m_Journal;int delivered=0;
|
||||
bool OriginOccupied(void*,ERR_HQUnit*){return false;}
|
||||
void Observe(ERR_HQUnit* u,float){if(!u->Uplink&&u->Radio&&u->visible&&m_Network.available){u->contact=true;u->Uplink=new ERR_HQRoute;}}
|
||||
@@ -60,6 +65,18 @@ Network m_Network;Journal m_Journal;int delivered=0;
|
||||
u.Uplink=nullptr;u.PendingStatus=nullptr;u.contact=false;}
|
||||
};
|
||||
int main(){
|
||||
// Mine scarcity/completeness alone never consumes the early status slot.
|
||||
for(int state=0;state<3;state++){
|
||||
HQ onlyMines;ERR_HQUnit squad;squad.NextStatus=180000;squad.StatusSampler.stock.MineMembers=4;
|
||||
squad.StatusSampler.stock.ReadyMines=state==2?2:0;squad.StatusSampler.stock.MineComplete=state!=1;
|
||||
onlyMines.QueueReports(&squad,0);assert(squad.contact&&!squad.PendingStatus&&onlyMines.m_Journal.writes==0);
|
||||
onlyMines.Finish(squad);squad.visible=false;squad.NextStatus=1000;onlyMines.QueueReports(&squad,1000);
|
||||
assert(squad.PendingStatus&&squad.PendingStatus->AmmoState->ReadyMines==(state==2?2:0)&&squad.PendingStatus->AmmoState->MineComplete==(state!=1)&&squad.PendingStatus->AmmoState->MineMembers==4);
|
||||
assert(squad.Uplink->Payload.find("; carried mines=")!=string::npos);
|
||||
if(state==1)assert(squad.Uplink->Payload.find("carried mines=unknown/incomplete")!=string::npos);
|
||||
auto sent=squad.PendingStatus->AmmoState;squad.StatusSampler.stock.ReadyMines=77;squad.StatusSampler.stock.MineMembers=0;squad.StatusSampler.stock.MineComplete=!sent->MineComplete;
|
||||
assert(sent->ReadyMines==(state==2?2:0)&&sent->MineComplete==(state!=1)&&sent->MineMembers==4);
|
||||
}
|
||||
// Exact inclusive threshold counts chambers, ignores launcher stock, requires the complete current living roster.
|
||||
ERR_HQAmmoReporting p; p.Initialize(6,0);ERR_HQStatusReport r;ERR_HQAmmoSnapshot a;r.AmmoState=&a;
|
||||
assert(!p.Urgent(&r,0));a.SmallArmsRounds=120;assert(p.Urgent(&r,0));
|
||||
@@ -67,7 +84,7 @@ int main(){
|
||||
a.SmallArmsRounds=0;a.SmallArmsChambers=0;a.LauncherRounds=100;assert(p.Urgent(&r,0));
|
||||
a.Complete=false;assert(!p.Urgent(&r,0));a.Complete=true;
|
||||
a.SmallArmsMuzzles=0;assert(!p.Urgent(&r,0));a.SmallArmsMuzzles=4;
|
||||
a.Members=3;assert(!p.Urgent(&r,0));r.Count=3;assert(p.Urgent(&r,0));
|
||||
a.Members=3;assert(!p.Urgent(&r,0));r.Count=3;r.Fit=3;assert(p.Urgent(&r,0));
|
||||
a.Members=0;r.Count=0;assert(!p.Urgent(&r,0));a.Members=33;r.Count=33;assert(!p.Urgent(&r,0));
|
||||
a.Members=4;r.Count=4;a.SmallArmsRounds=-1;assert(!p.Urgent(&r,0));a.SmallArmsRounds=0;
|
||||
r.ObservedAt=1;assert(!p.Urgent(&r,0));r.ObservedAt=0;assert(!p.Urgent(&r,1));
|
||||
@@ -80,15 +97,38 @@ int main(){
|
||||
assert(!phase.ScanDue(first+999999));}
|
||||
// A fresh complete low report uses the status-first shared uplink, never immediate delivered knowledge.
|
||||
HQ h;ERR_HQUnit u;u.StatusSampler.stock.SmallArmsRounds=0;u.NextStatus=180000;
|
||||
u.StatusSampler.mounted.Rounds=0;u.StatusSampler.mounted.LowMagazines=1;
|
||||
h.QueueReports(&u,0);assert(u.PendingStatus&&!u.contact&&h.delivered==0&&u.PendingStatus->MessageId>0);
|
||||
assert(u.Uplink->Payload.find("mounted ammunition=")!=string::npos);
|
||||
auto mounted=u.PendingStatus->MountedAmmo;u.StatusSampler.mounted.Rounds=100;assert(mounted->Rounds==0);
|
||||
auto pending=u.PendingStatus;h.QueueReports(&u,1000);assert(u.PendingStatus==pending&&u.StatusSampler.samples==1);
|
||||
h.Finish(u,false);assert(h.delivered==0);h.QueueReports(&u,30000);assert(u.contact&&!u.PendingStatus);
|
||||
h.Finish(u);h.QueueReports(&u,60000);assert(u.PendingStatus);h.Finish(u);assert(h.delivered==1);
|
||||
// Adequate/incomplete early samples consume no frame and leave room for positive contact or a quiet sweep.
|
||||
HQ healthy;ERR_HQUnit good;good.NextStatus=180000;
|
||||
// Healthy mounted stock does not queue an early status; infantry remains independent.
|
||||
healthy.QueueReports(&good,0);assert(good.contact&&!good.PendingStatus&&healthy.m_Journal.writes==0);
|
||||
healthy.Finish(good);good.visible=false;good.StatusSampler.stock.Complete=false;
|
||||
healthy.QueueReports(&good,30000);assert(good.sweep&&!good.Uplink);
|
||||
// Complete low mounted stock can request the same status frame even with healthy carried ammunition.
|
||||
HQ vehicle;ERR_HQUnit crew;crew.NextStatus=180000;crew.StatusSampler.mounted.Rounds=25;crew.StatusSampler.mounted.LowMagazines=1;
|
||||
vehicle.QueueReports(&crew,0);assert(crew.PendingStatus&&!crew.contact&&vehicle.delivered==0);
|
||||
assert(crew.PendingStatus->AmmoState->SmallArmsRounds==121&&crew.PendingStatus->MountedAmmo->Rounds==25);
|
||||
auto installed=crew.PendingStatus->MountedAmmo;crew.StatusSampler.mounted.Rounds=0;assert(installed->Rounds==25);
|
||||
vehicle.Finish(crew,false);vehicle.QueueReports(&crew,30000);assert(crew.contact&&!crew.PendingStatus);
|
||||
vehicle.Finish(crew);vehicle.QueueReports(&crew,60000);assert(crew.PendingStatus);vehicle.Finish(crew);assert(vehicle.delivered==1);
|
||||
// No mounted inference from incomplete/captured, unarmed, invalid or absent snapshots.
|
||||
ERR_HQAmmoReporting mountedPolicy;ERR_HQStatusReport mr;ERR_HQAmmoSnapshot carried;mr.AmmoState=&carried;
|
||||
ERR_HQMountedAmmoSnapshot vehicleStock;mr.MountedAmmo=&vehicleStock;vehicleStock.Rounds=25;vehicleStock.LowMagazines=1;
|
||||
assert(mountedPolicy.Urgent(&mr,0));vehicleStock.Complete=false;assert(!mountedPolicy.Urgent(&mr,0));vehicleStock.Complete=true;
|
||||
vehicleStock.Weapons=0;assert(!mountedPolicy.Urgent(&mr,0));vehicleStock.Weapons=1;
|
||||
vehicleStock.LowMagazines=2;assert(!mountedPolicy.Urgent(&mr,0));vehicleStock.LowMagazines=1;
|
||||
vehicleStock.Rounds=-1;assert(!mountedPolicy.Urgent(&mr,0));vehicleStock.Rounds=25;
|
||||
vehicleStock.LowMagazines=0;assert(!mountedPolicy.Urgent(&mr,0));vehicleStock.LowMagazines=1;
|
||||
mr.Count=0;assert(!mountedPolicy.Urgent(&mr,0));mr.Count=4;mr.Fit=0;assert(!mountedPolicy.Urgent(&mr,0));mr.Fit=4;
|
||||
mr.ObservedAt=-1;assert(!mountedPolicy.Urgent(&mr,0));mr.ObservedAt=0;
|
||||
carried.Complete=false;assert(mountedPolicy.Urgent(&mr,0));carried.SmallArmsRounds=-1;assert(!mountedPolicy.Urgent(&mr,0));
|
||||
carried.SmallArmsRounds=121;mr.AmmoState=nullptr;assert(mountedPolicy.Urgent(&mr,0));mr.MountedAmmo=nullptr;assert(!mountedPolicy.Urgent(&mr,0));
|
||||
// No route/radio means no snapshot or invented radio receipt. Early no-route attempts are bounded.
|
||||
HQ off;ERR_HQUnit absent;absent.NextStatus=180000;off.m_Network.available=false;
|
||||
off.QueueReports(&absent,0);off.QueueReports(&absent,1000);assert(off.m_Network.routes==1&&absent.StatusSampler.samples==0);
|
||||
@@ -111,4 +151,4 @@ int main(){
|
||||
assert(status==10&&contacts==90&&firing.StatusSampler.samples<=21);
|
||||
}
|
||||
''')
|
||||
print('PASS: actual early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures and requested check-ins')
|
||||
print('PASS: actual carried/mounted early ammunition policy and status-first shared uplink; complete threshold, phases, cooldown, failures requested check-ins and non-urgent detached carried-mine status')
|
||||
|
||||
Reference in New Issue
Block a user