Files
EscapistsReintroduced/tests/test_prison_placement_policy.py
T
jakub cab998fb64 Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
2026-09-20 16:42:52 +02:00

60 lines
2.5 KiB
Python

"""Execute the actual prison search body with a recording clearance predicate.
This verifies bounded search order/termination, not terrain or body clearance.
"""
from policy_harness import ROOT,body,run
source=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnPlacement.c'
search=(body(source,'bool Prison(').replace('map<int, vector> reservations','map<int, vector>* reservations')
.replace('out vector position','vector& position').replace('ERR_Actors.', 'ERR_Actors::').replace('Math.', 'Math::'))
run('''#include <cassert>
#include <cmath>
#include <vector>
#include <set>
#include <utility>
struct vector {float x,y,z;};
vector operator+(vector a,vector b){return {a.x+b.x,a.y+b.y,a.z+b.z};}
vector Vector(float x,float y,float z){return {x,y,z};}
template<class K,class V> struct map {};
namespace ERR_Actors {vector Ground(vector value){return value;}}
namespace Math {int AbsInt(int value){return std::abs(value);}}
struct Search {
std::vector<vector> visited;
int accept=-1;
bool Clear(vector candidate,map<int,vector>*){visited.push_back(candidate);return int(visited.size())-1==accept;}
''' + search + '''
};
int main(){
const vector center={6090,100,7160};
vector result={-1,-1,-1};
Search blocked;
assert(!blocked.Prison(center,nullptr,result));
assert(blocked.visited.size()==16);
assert(result.x==-1&&result.y==-1&&result.z==-1); // no outside fallback
std::set<std::pair<float,float>> unique;
for(int i=0;i<16;++i){
vector candidate=blocked.visited[i];
float x=candidate.x-center.x,z=candidate.z-center.z;
assert(std::abs(x)<=4.5f&&std::abs(z)<=4.5f);
assert(unique.insert({x,z}).second);
assert(std::abs(x)==1.5f||std::abs(x)==4.5f);
assert(std::abs(z)==1.5f||std::abs(z)==4.5f);
int outer=(std::abs(x)==4.5f)+(std::abs(z)==4.5f);
assert(outer==(i<4?0:(i<12?1:2)));
if(i<12)assert(std::hypot(x,z)<5); // inner and edge spawns meet prior 5m check
for(int j=0;j<i;++j){
vector previous=blocked.visited[j];
assert(std::hypot(candidate.x-previous.x,candidate.z-previous.z)>=3);
}
}
// Select any first available position; never bypass the native clearance predicate.
for(int accepted=0;accepted<16;++accepted){
Search search;search.accept=accepted;
assert(search.Prison(center,nullptr,result));
assert(search.visited.size()==unsigned(accepted+1));
vector expected=blocked.visited[accepted];
assert(result.x==expected.x&&result.y==expected.y&&result.z==expected.z);
}
}
''')
print('PASS: prison search packs 16 unique 3m-spaced inside-wall centers, prioritizes inner/edge positions and never bypasses clearance')