"""Actual delivered-search clock and bounded road selection, without engine navigation.""" import re from policy_harness import ROOT, body, run p = ROOT / 'addons/core/Scripts/Game/ERR/10_AI/ERR_HunterService.c' state = body(p, 'class ERR_HunterSearch').replace('class ERR_HunterSearch', 'struct ERR_HunterSearch') state = state.replace('vector ', 'Vec ').replace('int MessageId;', 'int MessageId=0;').replace('Math.Clamp', 'std::clamp') + ';' point = body(p, 'protected bool SearchPoint(').replace('protected ', '').replace('ERR_HunterVehicle unit', 'ERR_HunterVehicle* unit') point = point.replace('out vector ', 'Vec& ').replace('vector ', 'Vec ') point = point.replace('unit.', 'unit->').replace('Search.', 'Search->').replace('Entity.', 'Entity->') point = point.replace('Math.', 'Math::').replace('ERR_SupplyConvoySystem.', 'ERR_SupplyConvoySystem::').replace('vector.', 'Geometry::') run(r''' #include #include #include struct Vec{float x=0,y=0,z=0;Vec operator+(Vec b){return {x+b.x,y+b.y,z+b.z};}Vec operator*(float s){return {x*s,y*s,z*s};} float Length(){return std::hypot(x,z);}Vec Normalized(){return *this*(1/Length());}}; Vec Vector(float x,float y,float z){return {x,y,z};} namespace Geometry{float DistanceXZ(Vec a,Vec b){return std::hypot(a.x-b.x,a.z-b.z);}} namespace Math{float random=1;float RandomFloat(float,float){return random;}} '''+state+r''' struct Entity{Vec position;Vec GetOrigin(){return position;}}; struct ERR_HunterVehicle{ERR_HunterSearch search;ERR_HunterSearch* Search=&search;struct Entity entity;struct Entity* Entity=&entity;}; namespace ERR_SupplyConvoySystem{int calls=0;bool road=true;Vec projection;float radius=0; bool ApproachDirected(Vec seed,Vec& dst,Vec&,float distance){calls++;radius=distance;dst=seed+projection;return road;}} struct Service{ '''+point+r''' }; int main(){ ERR_HunterSearch search;assert(!search.Active(0));search.Remember({100,0,200},100,7); assert(!search.Active(99)&&search.Active(100)&&search.Active(600100)&&!search.Active(600101)); assert(search.Radius(100)==250&&search.Radius(300100)==550&&search.Radius(600100)==850); search.Remember({999,0,999},100,99);assert(search.Center.x==100&&search.MessageId==7); // replay cannot move/renew memory search.Remember({999,0,999},99,100);assert(search.ObservedAt==100); search.Remember({999,0,999},200,0);assert(search.ObservedAt==100); // not a delivered message assert(!search.Ready(false,1000)&&!search.Ready(true,2000)&&!search.Ready(true,61999)); assert(search.Ready(true,62000));assert(!search.Ready(false,63000));assert(!search.Ready(true,64000)); search.Remember({300,0,400},65000,8);assert(search.ArrivedAt==-1&&!search.Ready(true,65000)); assert(!search.Ready(true,665001)&&search.ArrivedAt==-1); // absolute observation deadline ERR_HunterVehicle unit;unit.Search->Remember({0,0,0},0,1);Service service;Vec dst,angles; assert(service.SearchPoint(&unit,600000,dst,angles));assert(std::abs(Geometry::DistanceXZ(dst,{})-850)<0.1); assert(ERR_SupplyConvoySystem::radius==100); ERR_SupplyConvoySystem::projection={1000,0,1000};int calls=ERR_SupplyConvoySystem::calls; assert(!service.SearchPoint(&unit,600000,dst,angles)&&ERR_SupplyConvoySystem::calls==calls+8); // projected roads cannot escape 1 km envelope ERR_SupplyConvoySystem::projection={};unit.entity.position={601,0,601};calls=ERR_SupplyConvoySystem::calls; assert(!service.SearchPoint(&unit,600000,dst,angles)&&ERR_SupplyConvoySystem::calls==calls+8); // avoid current road patch unit.entity.position={};ERR_SupplyConvoySystem::road=false;assert(!service.SearchPoint(&unit,600000,dst,angles)); Math::random=0;calls=ERR_SupplyConvoySystem::calls;assert(!service.SearchPoint(&unit,600000,dst,angles)&&ERR_SupplyConvoySystem::calls==calls); // degenerate vector safely rejected } ''') print('PASS: searches expire ten minutes after observation, ignore replays, wait at arrival, widen within 1 km and bound road queries')