Files
EscapistsReintroduced/tests/test_hunter_sector_search.py
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

51 lines
3.9 KiB
Python

"""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<float>') + ';'
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 <algorithm>
#include <cassert>
#include <cmath>
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')