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:
@@ -0,0 +1,91 @@
|
||||
"""Execute reserve decisions and commits from production, with detached HQ reports."""
|
||||
import re
|
||||
from policy_harness import ROOT, body, run
|
||||
|
||||
p = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterService.c'
|
||||
def translate(s):
|
||||
s = s.replace('protected ', '').replace('foreach (', 'for (').replace('null', 'nullptr')
|
||||
for typ in ('ERR_HQWatchEntry', 'ERR_HunterVehicle', 'ERR_HQ'):
|
||||
s = re.sub(r'\b' + typ + r' (?=\w)', typ + '* ', s)
|
||||
for name in ('entry', 'unit', 'hq', 'm_PendingReserve'):
|
||||
s = s.replace(name + '.', name + '->')
|
||||
return (s.replace('Watch().', 'Watch()->').replace('int count;', 'int count=0;')
|
||||
.replace('ERR_HunterFleetRules.', 'ERR_HunterFleetRules::')
|
||||
.replace('ERR_HQ.Event', 'ERR_HQ::Event').replace('string.Format', 'Format'))
|
||||
|
||||
rules = '\n'.join(translate(body(p, sig)) for sig in ('static bool Unavailable(', 'static bool ReserveDue('))
|
||||
methods = '\n'.join(translate(body(p, sig)) for sig in (
|
||||
'int Deployed(', 'protected int AvailableToHQ(', 'protected ERR_HunterVehicle ReserveSource(',
|
||||
'protected void CommitReserve('))
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
using string=std::string;
|
||||
template<class... T>string Format(string s,T...){return s;}
|
||||
struct ERR_HQWatchEntry{bool Closed=false;int Fit=3;float ObservedAt=-1;};
|
||||
struct Watch{std::map<string,ERR_HQWatchEntry*> entries;ERR_HQWatchEntry* Find(string s){return entries[s];}};
|
||||
struct ERR_HQ{struct Watch watch;struct Watch* Watch(){return &watch;}static void Event(string,string){}};
|
||||
struct ERR_HunterVehicle{bool Motorbike=false,ReserveCovered=false,Released=false;float LaunchedAt=0;string Source;};
|
||||
struct ERR_HunterFleetRules{
|
||||
'''+rules+r'''
|
||||
};
|
||||
struct Service{std::vector<ERR_HunterVehicle*> m_Hunters;int m_Target=3,m_ReserveSpent=0;
|
||||
float m_NextReserve=0;ERR_HunterVehicle* m_PendingReserve=nullptr;
|
||||
'''+methods+r'''
|
||||
};
|
||||
int main(){
|
||||
ERR_HQWatchEntry report;
|
||||
assert(!ERR_HunterFleetRules::Unavailable(nullptr,100,480099));
|
||||
assert(ERR_HunterFleetRules::Unavailable(nullptr,100,480100));
|
||||
report.ObservedAt=100000;assert(!ERR_HunterFleetRules::Unavailable(&report,100,579999));
|
||||
assert(ERR_HunterFleetRules::Unavailable(&report,100,580000));
|
||||
report.Fit=0;assert(ERR_HunterFleetRules::Unavailable(&report,100,101));
|
||||
report.Fit=3;report.Closed=true;assert(ERR_HunterFleetRules::Unavailable(&report,100,101));
|
||||
assert(!ERR_HunterFleetRules::Unavailable(&report,100,99));
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(3,3,0,2,0,480000));
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(3,4,0,0,0,480000)); // aggression deployment first
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(15,12,3,0,0,480000));
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(15,15,0,0,0,480000));
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(6,3,3,0,0,480000));
|
||||
assert(!ERR_HunterFleetRules::ReserveDue(4,3,1,0,960000,959999));
|
||||
assert(ERR_HunterFleetRules::ReserveDue(4,3,1,1,960000,960000));
|
||||
|
||||
Service s;ERR_HQ hq;ERR_HunterVehicle a,b,c,bike;a.Source="a";b.Source="b";c.Source="c";
|
||||
bike.Source="bike";bike.Motorbike=true;s.m_Hunters={&a,&b,&c,&bike};
|
||||
a.Released=true;assert(s.AvailableToHQ(&hq,100)==3); // physical loss alone reveals nothing
|
||||
assert(!s.ReserveSource(&hq,479999));
|
||||
assert(s.ReserveSource(&hq,480000)==&a); // absence of a first check-in has a bounded deadline
|
||||
report.Closed=false;report.ObservedAt=480000;hq.watch.entries["a"]=&report;hq.watch.entries["b"]=&report;
|
||||
assert(!s.ReserveSource(&hq,480000)); // fresh returning radios avert replacement
|
||||
report.Fit=0;assert(s.ReserveSource(&hq,480000)==&a); // delivered unfit crew can unlock reserve
|
||||
hq.watch.entries.clear();
|
||||
s.m_PendingReserve=s.ReserveSource(&hq,480000);
|
||||
assert(s.m_ReserveSpent==0&&!a.ReserveCovered); // choosing/placement deferral does not spend
|
||||
ERR_HunterVehicle replacement;replacement.Source="replacement";replacement.LaunchedAt=480000;
|
||||
s.m_Hunters.push_back(&replacement);s.CommitReserve(&replacement,480000);
|
||||
assert(s.m_ReserveSpent==1&&a.ReserveCovered&&s.m_NextReserve==960000&&!s.m_PendingReserve);
|
||||
s.CommitReserve(&replacement,480001);assert(s.m_ReserveSpent==1); // one commit per departure attempt
|
||||
assert(!s.ReserveSource(&hq,959999));assert(s.ReserveSource(&hq,960000)==&b);
|
||||
b.ReserveCovered=true;c.ReserveCovered=true;replacement.ReserveCovered=true;
|
||||
assert(!s.ReserveSource(&hq,960000)); // no re-use of a covered loss, bikes cannot unlock reserves
|
||||
|
||||
// A prolonged radio blackout admits exactly three reserves, eight minutes apart, then stops.
|
||||
Service outage;ERR_HQ silent;ERR_HunterVehicle originals[3],reserves[3];
|
||||
for(int i=0;i<3;i++){originals[i].Source="original"+std::to_string(i);outage.m_Hunters.push_back(&originals[i]);}
|
||||
for(int i=0;i<3;i++){
|
||||
float now=(i+1)*480000;assert(!outage.ReserveSource(&silent,now-1));
|
||||
outage.m_PendingReserve=outage.ReserveSource(&silent,now);assert(outage.m_PendingReserve);
|
||||
reserves[i].Source="reserve"+std::to_string(i);reserves[i].LaunchedAt=now;
|
||||
outage.m_Hunters.push_back(&reserves[i]);outage.CommitReserve(&reserves[i],now);
|
||||
}
|
||||
assert(outage.Deployed()==6&&!outage.ReserveSource(&silent,9999999));
|
||||
outage.m_Target=4;assert(!outage.ReserveSource(&silent,9999999)); // next ordinary escalation remains separate
|
||||
}
|
||||
''')
|
||||
|
||||
spawn = body(p, 'protected bool SpawnHunter(')
|
||||
assert spawn.index('ERR_EnemyLoadoutCatalog.Available') < spawn.index('CommitReserve(unit, now)')
|
||||
assert spawn.index('m_Hunters.Insert(unit)') < spawn.index('CommitReserve(unit, now)') < spawn.index('unit.Group =')
|
||||
print('PASS: reserve uses HQ reports/dispatch deadlines, preserves radio uncertainty, spends once, respects cooldown/availability/three-reserve/15-vehicle caps')
|
||||
Reference in New Issue
Block a user