Files
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

67 lines
8.5 KiB
Python

from pathlib import Path
P=Path('/tmp/erri-mounted-intercept-convoy-1247');R=Path('/home/jakub/devel/escapistsrereintroduced');(P/'tests').mkdir(exist_ok=True)
def put(name,s):
b=P/'baseline/tests'/name;b.parent.mkdir(parents=True,exist_ok=True);b.write_bytes((R/'tests'/name).read_bytes());(P/'tests'/name).write_text(s)
s=(R/'tests/test_convoy_lifecycle.py').read_text().replace('from policy_harness import ROOT,run','from policy_harness import ROOT,run,body')
s=s.replace(".replace('ref ','')", ".replace('ref ','').replace('protected ','')")
s=s.replace("'ERR_ConvoyCombatState']:","'ERR_ConvoyCombatState','ERR_ConvoyInterceptOrder']:")
s=s.replace("'route','Combat']:","'route','Combat','order','detour','m_InterceptWaypoint','m_InterceptGroup','InterceptOrder']:")
s=s.replace(".replace('Forces().','Forces()->')", ".replace('Seats[i].','Seats[i]->').replace('Forces().','Forces()->')")
s=s.replace('run(r\'\'\'','''service='\\n'.join(body(path, x) for x in ('protected bool InterceptEligible(', 'bool InterceptCurrent(', 'bool ReserveIntercept(', 'bool ApplyIntercept(', 'void ReleaseIntercept('))
service=service.replace('protected ','').replace('foreach (','for (').replace('null','nullptr')
for typ in ['ERR_ConvoyTask','ERR_ConvoyInterceptOrder','ERR_ConvoyVehicle','ERR_ForceRecord']:
service=re.sub(r'\\b'+typ+r' (?=\\w)',typ+'* ',service)
for obj in ['task','order','unit','record','other','existing','claimed','m_Mode']:
service=service.replace(obj+'.',obj+'->')
for obj in ['Receipt','Route']:
service=service.replace(obj+'.',obj+'->')
service=service.replace('Forces().','Forces()->').replace('task->Vehicles[order->VehicleIndex].','task->Vehicles[order->VehicleIndex]->')
service=service.replace('ERR_ForceRules.','ERR_ForceRules::').replace('ERR_ForceKind.','ERR_ForceKind::')
service='struct ERR_ConvoyReceipt{bool Consumed=true;std::string SourceId="original";Array<int> Records={7,8,9,10,11,12};}; struct ERR_ConvoyTask{bool Stopped=false,Ready=true;ERR_ConvoyReceipt* Receipt;ERR_ForceRoute* Route;Array<ERR_ConvoyVehicle*> Vehicles;};struct Service{ERR_GameMode* m_Mode;bool current=true;Array<ERR_ConvoyTask*>m_Tasks;bool Current(){return current;}'+service+'};'
run(r\'\'\'''' ,1)
s=s.replace('#include <cmath>', '#include <cmath>\n#include <string>')
s=s.replace('float x=0,y=0,z=0;static', 'float x=0,y=0,z=0;bool operator!=(vector b){return x!=b.x||y!=b.y||z!=b.z;}static')
s=s.replace('void Insert(T v)', 'int Count(){return this->size();}bool IsEmpty(){return this->empty();}void Insert(T v)')
s=s.replace('struct AIAgent{int lod=0;', 'struct AIAgent{IEntity* actor=nullptr;IEntity* GetControlledEntity(){return actor;}int lod=0;')
s=s.replace('struct BaseCompartmentSlot{};', 'struct BaseCompartmentSlot{IEntity* occupant=nullptr;IEntity* GetOccupant(){return occupant;}};')
s=s.replace('struct ERR_ForceRecord{bool Pinned=true;};','namespace ERR_ForceKind{const int CONVOY=3;}\nstruct ERR_ForceRecord{int Id=7,Kind=3;bool Physical=true,Eliminated=false,Pinned=true;};')
s=s.replace('namespace ERR_ConvoyRules{bool PartOf', 'namespace ERR_ConvoyRules{inline bool captured=false;bool Captured(Vehicle*,SCR_AIGroup*){return captured;}bool PartOf')
s=s.replace("''' + code + r'''", """namespace ERR_ForceRules{bool Finite(float n){return std::isfinite(n);}bool FiniteVector(vector p){return Finite(p.x)&&Finite(p.y)&&Finite(p.z);}}
struct ERR_ConvoyInterceptOrder{int Token=400001,VehicleIndex=0,BeliefId=1,BeliefRevision=1,ReportId=1;SCR_AIGroup* Group=nullptr;std::string SourceId="original";vector Position{700,0,0};float ObservedAt=1000,IssuedAt=1000,ExpiresAt=121000;};
''' + code + service + r'''""")
s=s.replace('AIAgent agent;ERR_ConvoyVehicle', 'AIAgent agent;BaseCompartmentSlot seat;ERR_ConvoyVehicle')
s=s.replace('Case(){unit.Entity=', 'Case(){agent.actor=&driver;seat.occupant=&driver;unit.Seats.Insert(&seat);unit.Registered=true;unit.Entity=')
s=s.replace('int main(){', '''struct InterceptCase:Case{Service service;ERR_ConvoyReceipt receipt;ERR_ConvoyTask task;ERR_ConvoyInterceptOrder order;
InterceptCase(){service.m_Mode=&mode;task.Receipt=&receipt;task.Route=&route;task.Vehicles={&unit};service.m_Tasks={&task};order.Group=&group;}
bool Reserve(){return service.ReserveIntercept(&task,&order,1000);}bool Apply(){return service.ApplyIntercept(&task,&order,2000);}
};
int main(){
{InterceptCase f;f.unit.Tick(&f.mode,&f.route,0);auto route=f.unit.Waypoint;assert(f.Reserve());assert(f.unit.Waypoint==route&&!f.unit.InterceptActive);
f.unit.Tick(&f.mode,&f.route,1500);assert(f.unit.Waypoint==route&&f.unit.RouteIndex==0);assert(f.Apply());assert(!f.unit.Waypoint&&f.unit.InterceptWaypoint&&f.unit.InterceptActive);
assert(!f.Apply());f.group.within=true;f.unit.Tick(&f.mode,&f.route,3000);assert(f.unit.RouteIndex==0);f.vehicle.position=f.order.Position;
f.unit.Tick(&f.mode,&f.route,4000);assert(f.unit.InterceptActive&&!f.unit.InterceptWaypoint&&f.unit.RouteIndex==0);
f.unit.Tick(&f.mode,&f.route,120999);assert(!f.unit.Waypoint);f.group.within=false;f.unit.Tick(&f.mode,&f.route,121000);assert(!f.unit.InterceptToken&&f.unit.Waypoint&&f.unit.RouteIndex==0);assert(!f.Reserve());}
{InterceptCase f;assert(f.Reserve());f.unit.Tick(&f.mode,&f.route,121000);assert(!f.unit.InterceptToken&&f.unit.Waypoint);}
{InterceptCase f;assert(f.Reserve());f.group.within=true;f.unit.Tick(&f.mode,&f.route,1100);f.unit.Tick(&f.mode,&f.route,1200);assert(f.unit.RouteIndex==1);assert(f.Apply());assert(f.unit.RouteIndex==1);}
for(int bad=0;bad<13;bad++){InterceptCase f;
if(bad==0)f.order.Token=300001;if(bad==1)f.order.VehicleIndex=1;if(bad==2)f.order.ExpiresAt=121001;
if(bad==3)f.order.Position.x=NAN;if(bad==4)f.order.IssuedAt=1001;if(bad==5)f.unit.Arrived=true;
if(bad==6)f.unit.SupplyToken=100001;if(bad==7)f.unit.SupplyHeld=true;if(bad==8)f.unit.Combat->Held=true;
if(bad==9)f.seat.occupant=nullptr;if(bad==10)f.mode.ledger.record.Id=99;if(bad==11)f.agent.actor=nullptr;if(bad==12)f.service.current=false;
assert(!f.Reserve()&&!f.unit.InterceptToken&&!f.unit.InterceptWaypoint);}
{InterceptCase f;assert(f.Reserve());assert(f.Apply());f.driver.alive=false;f.unit.Tick(&f.mode,&f.route,3000);assert(f.unit.Released&&!f.unit.InterceptToken&&!f.unit.InterceptWaypoint);}
{InterceptCase f;assert(f.Reserve());assert(f.Apply());f.unit.Members.Clear();f.unit.Tick(&f.mode,&f.route,3000);assert(!f.unit.InterceptToken&&!f.unit.InterceptWaypoint);}
{InterceptCase f;assert(f.Reserve());auto foreign=f.order;foreign.Token=400002;f.service.ReleaseIntercept(&f.task,&foreign,2000);assert(f.unit.InterceptToken==400001);
f.unit.InterceptToken=400002;f.service.ReleaseIntercept(&f.task,&f.order,2000);assert(f.unit.InterceptToken==400002);}
{InterceptCase f;assert(f.Reserve());assert(f.Apply());AIWaypoint foreign;f.unit.InterceptWaypoint=&foreign;f.service.ReleaseIntercept(&f.task,&f.order,3000);assert(f.unit.InterceptWaypoint==&foreign&&!f.unit.InterceptToken);}
{InterceptCase f;assert(f.Reserve());f.order.Position.x++;assert(!f.Apply());f.service.ReleaseIntercept(&f.task,&f.order,3000);assert(!f.unit.InterceptToken&&!f.unit.Waypoint);}
{InterceptCase f;assert(f.Reserve());ERR_Actors::fail=true;assert(!f.Apply()&&!f.unit.InterceptActive);ERR_Actors::fail=false;f.service.ReleaseIntercept(&f.task,&f.order,3000);assert(!f.unit.InterceptToken);}
''',1)
put('test_convoy_lifecycle.py',s)
# Existing actual combat/circuit/column programs keep all original assertions.
s=(R/'tests/test_convoy_combat.py').read_text().replace("'agent','m_Mode']", "'agent','m_Mode','intercept']").replace('int Index=0,RouteIndex=2,','int InterceptToken=0,Index=0,RouteIndex=2,');put('test_convoy_combat.py',s)
s=(R/'tests/test_convoy_column.py').read_text().replace('import re','import re,os\nfrom pathlib import Path',1).replace("p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SupplyConvoyColumn.c'", "p=Path(os.environ.get('ERR_SUPPLY_COLUMN_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_SupplyConvoyColumn.c'))")
s=s.replace("s=s.replace('m_Task->Vehicles[index].'", "s=s.replace('m_State[index].', 'm_State[index]->')\n s=s.replace('m_Task->Vehicles[index].'")
s=s.replace('bool Released=false,Arrived=false,SupplyHeld=false;', 'bool InterceptActive=false,Released=false,Arrived=false,SupplyHeld=false;');put('test_convoy_column.py',s)
s=(R/'tests/test_supply_convoys.py').read_text().replace("body(D/'ERR_SupplyConvoyService.c','protected void Circuit('","body(Path(os.environ.get('ERR_SUPPLY_SERVICE_SOURCE',D/'ERR_SupplyConvoyService.c')),'protected void Circuit('").replace('struct ERR_ConvoyVehicle{bool Released=false,Arrived=false,SupplyHeld=false;', 'struct ERR_ConvoyVehicle{int InterceptToken=0;bool Released=false,Arrived=false,SupplyHeld=false;');put('test_supply_convoys.py',s)