Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
"""Execute actual delivered-belief pursuit rules as C++; no engine/AI behavior claim."""
|
||||
import re
|
||||
import os, re
|
||||
from pathlib import Path
|
||||
from policy_harness import ROOT, body, run
|
||||
P = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'
|
||||
P = Path(os.environ.get('ERR_HQ_PURSUIT_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQPursuit.c'))
|
||||
source = P.read_text()
|
||||
# Exercise the real planner expression against the same actual policy methods.
|
||||
HQ = Path(os.environ.get('ERR_HQ_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
|
||||
assignment = body(HQ, 'protected void Assign(')
|
||||
selection = re.search(r'ERR_HQPursuitRole role = (ERR_HQPursuitRules\.Role\([^;]+\));', assignment)
|
||||
assert selection, 'missing actual Assign role selection'
|
||||
planner_role = selection.group(1).replace('ERR_HQPursuitRules.', 'Rules::')
|
||||
methods = '\n'.join(body(P, signature) for signature in (
|
||||
'static bool Finite(', 'static bool PositionValid(', 'static int Limit(', 'static bool Actionable(',
|
||||
'static bool Heading(', 'static ERR_HQPursuitRole Role(', 'static float LifetimeSeconds(',
|
||||
'static float TaskLifetimeSeconds(', 'static bool CanCommit(', 'static int Bearing(', 'static bool BearingAllowed(', 'static vector Direction(',
|
||||
'static float SearchRadius(', 'static float TaskLifetimeSeconds(', 'static bool CanCommit(', 'static int Bearing(', 'static bool BearingAllowed(', 'static vector Direction(',
|
||||
'static vector Objective(', 'static float Score(', 'static bool Better('))
|
||||
methods = methods.replace('Math.', 'Math::').replace('vector.', 'vector::')
|
||||
methods = methods.replace('ERR_HQPursuitRole.', 'ERR_HQPursuitRole::')
|
||||
@@ -44,8 +51,14 @@ vector Vector(float a,float b,float c){return {a,b,c};}
|
||||
struct Rules {
|
||||
'''+methods+r'''
|
||||
};
|
||||
struct Belief { float Confidence=.9f, Radius=50; vector Velocity; bool HasVelocity=true; };
|
||||
ERR_HQPursuitRole PlannerRole(Belief belief,float age,int slot){return '''+planner_role+r''';}
|
||||
bool near(float a,float b){return std::abs(a-b)<0.001f;}
|
||||
int main(){
|
||||
assert(Rules::SearchRadius(0)==45&&Rules::SearchRadius(44.99)==45&&Rules::SearchRadius(45)==45);
|
||||
assert(Rules::SearchRadius(73)==73&&Rules::SearchRadius(110)==110&&Rules::SearchRadius(2000)==110);
|
||||
assert(Rules::SearchRadius(-1)==0&&Rules::SearchRadius(std::numeric_limits<float>::quiet_NaN())==0&&Rules::SearchRadius(std::numeric_limits<float>::infinity())==0);
|
||||
|
||||
using Role=ERR_HQPursuitRole;
|
||||
assert(Rules::Limit(-1)==0&&Rules::Limit(0)==0&&Rules::Limit(1)==1);
|
||||
assert(Rules::Limit(3)==3&&Rules::Limit(8)==3);
|
||||
@@ -64,6 +77,23 @@ int main(){
|
||||
assert(Rules::Role(.9,10,50,true,0)==Role::INTERCEPT);
|
||||
assert(Rules::Role(.9,10,50,true,1)==Role::SEARCH);
|
||||
assert(Rules::Role(.9,10,50,true,2)==Role::CONTAIN);
|
||||
// Memory can retain a valid slow-motion sample below the directed-task threshold.
|
||||
// The actual Assign expression must not queue containment that delivery rejects.
|
||||
Belief belief;
|
||||
for(float speed:{0.f,.49f,70.01f})for(int slot=0;slot<3;slot++){
|
||||
belief.Velocity={0,0,speed};
|
||||
assert(PlannerRole(belief,10,slot)==Role::SEARCH);
|
||||
}
|
||||
for(float speed:{.5f,70.f}){
|
||||
belief.Velocity={0,0,speed};
|
||||
assert(PlannerRole(belief,10,0)==Role::INTERCEPT);
|
||||
assert(PlannerRole(belief,10,1)==Role::SEARCH);
|
||||
assert(PlannerRole(belief,10,2)==Role::CONTAIN);
|
||||
}
|
||||
belief.Velocity={nan,0,5};
|
||||
assert(PlannerRole(belief,10,2)==Role::SEARCH);
|
||||
belief.Velocity=north;belief.HasVelocity=false;
|
||||
assert(PlannerRole(belief,10,0)==Role::SEARCH&&PlannerRole(belief,10,2)==Role::SEARCH);
|
||||
for(int slot=0;slot<3;slot++){
|
||||
assert(Rules::Role(.64,10,50,true,slot)==Role::SEARCH);
|
||||
assert(Rules::Role(.9,46,50,true,slot)==Role::SEARCH);
|
||||
@@ -79,7 +109,11 @@ int main(){
|
||||
assert(Rules::TaskLifetimeSeconds(Role::SEARCH,0)==120);
|
||||
assert(near(Rules::TaskLifetimeSeconds(Role::SEARCH,2000),2000.f/3+45));
|
||||
assert(Rules::TaskLifetimeSeconds(Role::SEARCH,5000)==900);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::INTERCEPT,2000)==60);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::INTERCEPT,0)==180);
|
||||
assert(near(Rules::TaskLifetimeSeconds(Role::INTERCEPT,2000),2000.f/3+180));
|
||||
assert(Rules::TaskLifetimeSeconds(Role::INTERCEPT,5000)==900);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::INTERCEPT,-1)==0);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::INTERCEPT,nan)==0);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::CONTAIN,2000)==90);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::SEARCH,-1)==0);
|
||||
assert(Rules::TaskLifetimeSeconds(Role::SEARCH,nan)==0);
|
||||
@@ -160,3 +194,27 @@ for forbidden in ('GetGame(', 'GetOrigin(', 'GetPlayers(', 'GetControlledEntity(
|
||||
assert forbidden not in source, f'pursuit must consume snapshots only: {forbidden}'
|
||||
assert all(len(line)<120 for line in source.splitlines())
|
||||
print('PASS: actual bounded pursuit roles/objectives/scores, stale search degradation, physical/busy/home guards, finite lifetime and two-bearing escape gap; no hidden player reads or spawning')
|
||||
|
||||
for signature in ('protected void Assign(', 'protected void Retask('):
|
||||
section=body(HQ,signature)
|
||||
line=next(line.strip() for line in section.splitlines() if 'task.SearchRadiusMetres =' in line)
|
||||
line=line.replace('ERR_HQPursuitRules.', 'Rules::').replace('ERR_HQPursuitRole.', 'Role::')
|
||||
run(r"""
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
enum class Role {SEARCH,INTERCEPT,CONTAIN};
|
||||
struct Math{static float Clamp(float n,float a,float b){return std::clamp(n,a,b);}};
|
||||
struct Rules{
|
||||
"""+body(P,'static bool Finite(')+body(P,'static float SearchRadius(').replace('Math.','Math::')+r"""
|
||||
};
|
||||
struct Task{float SearchRadiusMetres=0;};struct Belief{float Radius=0;};
|
||||
void Queue(Task& task,Belief& belief,Role role,bool roadIntercept){
|
||||
"""+line+r"""
|
||||
}
|
||||
int main(){for(float radius:{0.f,60.f,500.f}){Task task;Belief belief{radius};Queue(task,belief,Role::SEARCH,false);float copied=Rules::SearchRadius(radius);assert(task.SearchRadiusMetres==copied);belief.Radius=1900;assert(task.SearchRadiusMetres==copied);}
|
||||
"""+(r"""for(Role role:{Role::INTERCEPT,Role::CONTAIN}){Task task;Belief belief{100};Queue(task,belief,role,false);assert(task.SearchRadiusMetres==0);}Task road;Belief belief{100};Queue(road,belief,Role::SEARCH,true);assert(road.SearchRadiusMetres==0);""" if 'Assign' in signature else '')+r"""
|
||||
}
|
||||
""")
|
||||
print('PASS: actual finite45..110 radius and both ordinary search queue snapshot expressions')
|
||||
|
||||
Reference in New Issue
Block a user