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.
82 lines
4.6 KiB
Python
82 lines
4.6 KiB
Python
"""Execute the actual bounded HQ report scheduler; radio integration stays native."""
|
|
from policy_harness import ROOT, body, run
|
|
from pathlib import Path
|
|
import os
|
|
source = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQObservation.c'
|
|
hq = Path(os.environ.get('ERR_HQ_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
|
|
select = body(source, 'ERR_HQVisualContact Select(')
|
|
select = select.replace('array<ref ERR_HQVisualContact> samples', 'Array<ERR_HQVisualContact> samples')
|
|
select = select.replace('ERR_HQVisualContact selected;', 'ERR_HQVisualContact selected = nullptr;')
|
|
select = select.replace('foreach (ERR_HQVisualContact sample : samples)', 'for (ERR_HQVisualContact sample : samples)')
|
|
select = select.replace('sample.Entity', 'sample->Entity')
|
|
queued = body(source, 'void Queued(')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
struct Entity {int id;}; using IEntity=Entity*;
|
|
struct Sample {IEntity Entity;}; using ERR_HQVisualContact=Sample*;
|
|
template<class T> struct Array: std::vector<T> {
|
|
using std::vector<T>::vector;
|
|
int Count(){return this->size();}
|
|
int Find(T a){auto i=std::find(this->begin(),this->end(),a);return i==this->end()?-1:int(i-this->begin());}
|
|
void Remove(int i){this->erase(this->begin()+i);}
|
|
void Insert(T a){this->push_back(a);}
|
|
};
|
|
struct Selection {
|
|
Array<IEntity> m_Recent;
|
|
'''+select+queued+r'''
|
|
};
|
|
int main(){
|
|
Entity actors[80]{}; Sample samples[80]{};
|
|
for(int i=0;i<80;i++){actors[i].id=i;samples[i].Entity=&actors[i];}
|
|
Array<ERR_HQVisualContact> visible{&samples[0],&samples[1],&samples[2]};
|
|
Selection choice;
|
|
assert(!choice.Select({}));assert(!choice.Select({nullptr}));
|
|
Sample invalid{nullptr};assert(!choice.Select({&invalid}));
|
|
// Failed route allocation doesn't consume a turn: repeated Select is read only.
|
|
for(int i=0;i<5;i++)assert(choice.Select(visible)==&samples[0]);
|
|
assert(choice.m_Recent.empty());choice.Queued(nullptr);assert(choice.m_Recent.empty());
|
|
// Stable native order: every visible actor is reported before anyone repeats.
|
|
for(int i=0;i<30;i++){
|
|
auto selected=choice.Select(visible);assert(selected==&samples[i%3]);
|
|
choice.Queued(selected->Entity);assert(choice.m_Recent.Count()==3||i<2);
|
|
}
|
|
// Reordering native targets does not reset fairness. New contacts take priority.
|
|
assert(choice.Select({&samples[2],&samples[1],&samples[0]})==&samples[0]);
|
|
assert(choice.Select({&samples[0],&samples[3]})==&samples[3]);
|
|
choice.Queued(&actors[3]);
|
|
// Lost sight never reports a remembered actor; only current samples can win.
|
|
assert(choice.Select({&samples[2]})==&samples[2]);
|
|
// Interrupted delivery counts as an attempt, allowing another current contact.
|
|
choice.Queued(&actors[0]);assert(choice.Select(visible)==&samples[1]);
|
|
choice.Queued(&actors[1]);assert(choice.Select(visible)==&samples[2]);
|
|
// Repeated queue identity is deduplicated, and vanished references are purged.
|
|
choice.Queued(&actors[1]);assert(choice.m_Recent.Count()==4);
|
|
choice.m_Recent[0]=nullptr;choice.Queued(&actors[4]);
|
|
assert(choice.m_Recent.Find(nullptr)==-1);
|
|
// History and full 64-contact scheduling remain bounded under turnover.
|
|
Selection many;Array<ERR_HQVisualContact> sixtyFour;
|
|
for(int i=0;i<64;i++)sixtyFour.Insert(&samples[i]);
|
|
for(int i=0;i<192;i++){
|
|
auto selected=many.Select(sixtyFour);assert(selected==&samples[i%64]);
|
|
many.Queued(selected->Entity);assert(many.m_Recent.Count()<=64);
|
|
}
|
|
for(int i=64;i<80;i++)many.Queued(&actors[i]);
|
|
assert(many.m_Recent.Count()==64&&many.m_Recent.Find(&actors[0])==-1);
|
|
// Scheduling is per squad/round, never shared HQ intelligence.
|
|
Selection other;assert(other.Select(visible)==&samples[0]);
|
|
}
|
|
''')
|
|
observe=body(hq,'protected void Observe(')
|
|
assert 'samples[0]' not in observe
|
|
assert observe.index('ERR_HQObservation.Gather(')<observe.index('unit.ReportSelection.Select(samples)')
|
|
assert observe.index('unit.Uplink || now < unit.NextReport')<observe.index('unit.ReportSelection.Select(samples)')
|
|
assert observe.index('if (!route) return;')<observe.index('unit.PendingReport = report;')<observe.index('unit.Uplink = route;')<observe.index('unit.ReportSelection.Queued(seen.Entity)')
|
|
assert observe.count('unit.ReportSelection.Queued(')==1
|
|
assert 'Accept(' not in observe and 'AddWaypoint' not in observe
|
|
assert 'report.ObservedAt = seen.ObservedAt;' in observe
|
|
assert 'ERR_HQObservation.Describe(report, samples, now);' in observe
|
|
assert 'ref ERR_HQReportSelection ReportSelection = new ERR_HQReportSelection();' in hq.read_text()
|
|
print('PASS: actual per-squad bounded report selector rotates visible targets, survives ordering/turnover, records only queued attempts, and preserves radio intake ordering')
|