75 lines
4.4 KiB
Python
75 lines
4.4 KiB
Python
"""Execute recovery deadlines and cross-assignment rejection memory from production."""
|
|
from policy_harness import ROOT, body, run
|
|
|
|
ai = ROOT / 'addons/core/Scripts/Game/ERR/10_AI'
|
|
wait = body(ai / 'ERR_HunterCrewRecovery.c', 'class ERR_HunterRecoveryWait')
|
|
run('#include <cassert>\nstruct ' + wait.removeprefix('class ') + r''';
|
|
int main() {
|
|
ERR_HunterRecoveryWait wait;
|
|
assert(!wait.Expired(false,false,false,0,1000)); // admission owns its own deadline
|
|
assert(!wait.Expired(true,false,false,0,2000));
|
|
assert(!wait.Expired(true,false,false,0,121999));
|
|
assert(wait.Expired(true,false,false,0,122000));
|
|
// Repeated combat/seat-failure transitions cannot restart the deadline.
|
|
assert(!wait.Expired(true,false,true,0,123000));
|
|
assert(!wait.Expired(true,false,false,4,124000));
|
|
assert(!wait.Expired(true,false,false,-1,125000)); // no physics sample
|
|
assert(wait.Expired(true,false,false,3,126000));
|
|
assert(!wait.Expired(true,true,false,0,127000)); // recovered or adopted driver
|
|
assert(!wait.Expired(true,false,false,0,200000));
|
|
assert(!wait.Expired(true,false,false,0,319999));
|
|
assert(wait.Expired(true,false,false,0,320000));
|
|
wait.Reset(); // deliberate tactical dismount owns its separate time budget
|
|
assert(!wait.Expired(true,false,false,0,500000));
|
|
assert(!wait.Expired(true,false,false,0,1000)); // world clock reset
|
|
assert(!wait.Expired(true,false,false,0,120999));
|
|
assert(wait.Expired(true,false,false,0,121000));
|
|
}
|
|
''')
|
|
|
|
memory = body(ai / 'ERR_HunterTactics.c', 'class ERR_HunterApproachMemory')
|
|
memory = memory.replace('protected ', '').replace('ref array<vector>', 'Array<Vec>').replace('ref array<float>', 'Array<float>')
|
|
memory = memory.replace('array<vector> excluded', 'Array<Vec>& excluded').replace('foreach (vector position :', 'for (Vec position :')
|
|
memory = memory.replace('vector position', 'Vec position').replace('vector.DistanceXZ', 'Distance').replace('float RetryAfter;', 'float RetryAfter = 0;')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <cmath>
|
|
struct Vec {float x=0;}; float Distance(Vec a,Vec b){return std::abs(a.x-b.x);}
|
|
template<class T>struct Array:std::vector<T>{
|
|
int Count(){return this->size();}void Remove(int i){this->erase(this->begin()+i);}void Insert(T value){this->push_back(value);}
|
|
};
|
|
struct ''' + memory.removeprefix('class ') + r''';
|
|
int main(){
|
|
ERR_HunterApproachMemory crew;crew.Reject({100},1000);
|
|
assert(crew.RetryAfter==61000);
|
|
Array<Vec> nextAssignment;crew.Append(nextAssignment,9000);
|
|
assert(nextAssignment.size()==1&&nextAssignment[0].x==100); // eight seconds after release
|
|
crew.Reject({101},2000);Array<Vec> refreshed;crew.Append(refreshed,301000);
|
|
assert(refreshed.size()==1); // nearly identical stage refreshes, does not duplicate
|
|
Array<Vec> expired;crew.Append(expired,302000);assert(expired.empty());
|
|
for(int i=0;i<12;i++)crew.Reject({float(i*100)},400000+i);
|
|
Array<Vec> bounded;crew.Append(bounded,400020);assert(bounded.size()==8&&bounded[0].x==400);
|
|
ERR_HunterApproachMemory otherCrew;Array<Vec> independent;otherCrew.Append(independent,400020);assert(independent.empty());
|
|
Array<Vec> cleared;crew.Append(cleared,700011);assert(cleared.empty());
|
|
}
|
|
''')
|
|
|
|
# Wiring guards complement execution: the blocked-seat branch must reach this
|
|
# clock before normal travel's invalid-crew early return, and every planner
|
|
# candidate (including already parked vehicles) must consult shared memory.
|
|
service = (ai / 'ERR_HunterService.c').read_text()
|
|
tick = body(ai / 'ERR_HunterService.c', 'override void Tick(float now)')
|
|
assert tick.index('unit.Recovery.Wait.Expired(') < tick.index('if (!unit.CrewValid(m_Mode)) continue;')
|
|
assert tick.count('unit.Recovery.Wait.Reset()') == 2
|
|
assert 'AbandonStuckVehicle(unit, "driver recovery timed out after two minutes")' in tick
|
|
assert 'ref ERR_HunterApproachMemory FailedApproaches' in service
|
|
plan = body(ai / 'ERR_HunterTactics.c', 'bool Plan(array<vector> occupied)')
|
|
assert 'if (!Delivered && now < Unit.FailedApproaches.RetryAfter) return false;' in plan
|
|
assert 'Unit.FailedApproaches.Append(excluded, now);' in plan
|
|
assert plan.count('approachForward, excluded)') == 2
|
|
assert 'foreach (vector failedPosition : excluded)' in plan
|
|
assert 'foreach (vector reserved : excluded)' in plan
|
|
assert 'Unit.FailedApproaches.Reject(Stage, now);' in body(ai / 'ERR_HunterTactics.c', 'protected void RejectStage(')
|
|
print('PASS: blocked crew deadline survives combat interruptions, defers unsafe exits, resets after recovery; failed stages survive assignments with bounded expiry')
|