Files
EscapistsReintroduced/tests/test_shore_boats.py
T
2026-11-07 00:32:01 +01:00

83 lines
6.0 KiB
Python

"""Execute production finite boat-worker decisions with engine calls doubled."""
from policy_harness import ROOT,body,run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ShoreBoats.c'
step=body(p,'protected void Step(').replace('protected ','').replace('Replication.','Replication::').replace('ERR_GameMode.Get()','ERR_GameMode::Get()').replace('ERR_MissionRules.','ERR_MissionRules::').replace('m_Mode.','m_Mode->').replace('Math.RandomInt','RandomInt').replace('ERR_ShoreMap.Chance','Chance')
chance=body(ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ShoreMap.c','static int Chance(').replace('Math.Max','std::max')
run(r'''
#include <cassert>
#include <vector>
#include <string>
#include <algorithm>
using vector=int;
namespace Replication {bool server=true;bool IsServer(){return server;}}
namespace ERR_MissionRules {bool Terminal(int phase){return phase==2;}}
struct ERR_GameMode {int phase=0;bool Active(){return phase==1;}int Phase(){return phase;}static ERR_GameMode* Get();} mode;
ERR_GameMode* ERR_GameMode::Get(){return &mode;}
struct Array:std::vector<int>{using std::vector<int>::vector;bool IsEmpty(){return empty();}int Count(){return size();}void Remove(int i){erase(begin()+i);}};
struct Map {Array Edges={1,2,3,4,5};bool ready=false;int work=0;bool Ready(){return ready;}void Step(){work++;}};
int roll=10;int RandomInt(int,int end){if(end==100)return roll;return 0;}
''' + chance + r'''
struct Worker {
ERR_GameMode* m_Mode=&mode;Map m_Map;bool m_Stopped=false,clear=true,cross=false,success=true;
int m_Target=2,m_Attempts=0,m_Chance=20,m_CrossingChance=70,created=0,spawns=0;std::string m_State;
int Count(){return created;}void Stop(){m_Stopped=true;}
bool Survey(int,vector&,vector&,bool& crossing){crossing=cross;return clear;}
bool Spawn(vector,vector){spawns++;if(success)created++;return success;}
''' + step + r'''
};
int main(){
Worker w;for(int i=0;i<100;i++){w.Step();}assert(w.m_Map.work==0&&!w.m_Stopped);
mode.phase=1;w.Step();assert(w.m_Map.work==1&&w.spawns==0);w.m_Map.ready=true;
for(int i=0;i<100;i++){w.Step();}assert(w.spawns==2&&w.created==2&&w.m_Stopped);
Worker ordinary;ordinary.m_Map.ready=true;roll=50;for(int i=0;i<20;i++){ordinary.Step();}assert(ordinary.spawns==0&&ordinary.m_Attempts==5&&ordinary.m_Stopped);
Worker facing;facing.m_Map.ready=true;facing.cross=true;for(int i=0;i<20;i++){facing.Step();}assert(facing.created==2);
Worker blocked;blocked.m_Map.ready=true;blocked.clear=false;for(int i=0;i<20;i++){blocked.Step();}assert(blocked.m_Stopped&&blocked.spawns==0);
Worker failed;failed.m_Map.ready=true;failed.success=false;roll=10;for(int i=0;i<20;i++){failed.Step();}assert(failed.spawns==5&&failed.m_Stopped);
Worker bounded;bounded.m_Map.ready=true;bounded.m_Attempts=4096;bounded.Step();assert(bounded.m_Stopped&&bounded.spawns==0);
Worker client;Replication::server=false;client.Step();assert(client.m_Stopped&&client.m_Map.work==0);
Replication::server=true;mode.phase=2;Worker ended;ended.Step();assert(ended.m_Stopped&&ended.spawns==0);
}
''')
# The audited integration selects only the playable unarmed Motorboat, never scenery,
# armed alternatives or a boat's child light entity.
candidate=body(p,'static bool Candidate(').replace('string ','Text ')
run(r'''
#include <cassert>
#include <string>
#include <algorithm>
struct Text:std::string {using std::string::string;void ToLower(){std::transform(begin(),end(),begin(),::tolower);}bool EndsWith(std::string suffix){return size()>=suffix.size()&&compare(size()-suffix.size(),suffix.size(),suffix)==0;}};
'''+candidate+r'''
int main(){
assert(Candidate("{ABC}Prefabs/Vehicles/Boats/Motorboat/Motorboat_Base.et"));
for(auto s:{"Prefabs/Vehicles/RHIB/REAPER_RHIB_BK_ARMED.et","Prefabs/Vehicles/RHIB/REAPER_RHIB_Wreck.et","Prefabs/Vehicles/RHIB/REAPER_RHIB_LightEnt.et","Prefabs/Vehicles/Boats/Motorboat/Lights/Left_Red_Nav_ES.et","Prefabs/Vehicles/Boats/MOD_Boat_Base.et","Prefabs/Props/Boat.et"})assert(!Candidate(s));
}
''')
print('PASS: preparation/client/end guards, finite shuffled candidates, probability once per edge, no replenishment, failed placement bounds and unarmed boat catalog')
start=body(p,'void Start(').replace('Replication.','Replication::').replace('m_Mode.Settings().Get(', 'settings.Get(').replace('vector low, high; GetGame().GetWorldEntity().GetWorldBounds(low, high);','vector low=0, high=100;').replace('GetGame().GetCallqueue().CallLater(Step, 100, true)','schedules++')
run(r'''
#include <cassert>
#include <string>
#include <map>
using vector=int;
namespace Replication{bool server=true;bool IsServer(){return server;}}
struct Settings{std::map<std::string,int> v={{"shoreBoatCount",6},{"shoreBoatChancePercent",20},{"shoreBoatCrossingChancePercent",70}};int Get(std::string key){return v.at(key);}};
struct Map{int setups=0;void Setup(vector,vector){setups++;}};
struct Pool{bool empty=true;bool IsEmpty(){return empty;}};
struct Catalog{Pool Prefabs;bool loaded=false;int finds=0;void Find(){finds++;Prefabs.empty=!loaded;}};
struct Service{
bool m_Started=false,m_Stopped=false,m_Mode=true;int m_Target=0,m_Chance=0,m_CrossingChance=0,schedules=0;std::string m_State;
Settings settings;Catalog m_Catalog;Map m_Map;
''' + start + r'''
};
int main(){
Service normal;normal.m_Catalog.loaded=true;normal.Start();normal.Start();assert(normal.schedules==1&&normal.m_Map.setups==1&&normal.m_Target==6&&normal.m_CrossingChance==70&&normal.m_Chance==20);
normal.settings.v["shoreBoatCount"]=0;normal.Start();assert(normal.m_Target==6); // saved edits wait until next round
Service disabled;disabled.settings.v["shoreBoatCount"]=0;disabled.Start();assert(disabled.schedules==0&&disabled.m_Catalog.finds==0&&disabled.m_State=="disabled");
Service missing;missing.Start();assert(missing.schedules==0&&missing.m_Map.setups==0&&missing.m_State=="Motorboat not loaded");
Service stopped;stopped.m_Stopped=true;stopped.Start();assert(stopped.schedules==0&&stopped.m_Catalog.finds==0);
Service client;Replication::server=false;client.Start();assert(client.schedules==0&&client.m_Catalog.finds==0);
}
''')
print('PASS: actual startup snapshots settings once, skips disabled/missing-mod surveys, and refuses client/stopped scheduling')