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,10 +1,19 @@
|
||||
"""Execute actual HQ assignment ownership and failed-radio backoff bodies."""
|
||||
from policy_harness import ROOT, body, run
|
||||
import re
|
||||
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'
|
||||
import os,re
|
||||
from pathlib import Path
|
||||
p=Path(os.environ.get('ERR_HQ_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_HQ.c'))
|
||||
unit='\n'.join(body(p,s) for s in ('void CancelPending(', 'ERR_HQTask PlannedTask(', 'void ReleaseAssignment(', 'void Release()'))
|
||||
lost='\n'.join(body(p,s) for s in ('protected void FinishWatch(', 'protected void LostOrder(')).replace('protected ', '')
|
||||
if 'protected float HuntCommitment(' in p.read_text(): lost=body(p,'protected float HuntCommitment(').replace('protected ', '')+'\n'+lost
|
||||
if 'protected void ReconsiderHunt(' in p.read_text(): lost=body(p,'protected void ReconsiderHunt(').replace('protected ', '')+'\n'+lost
|
||||
if 'protected void RememberCompletedHunt(' in p.read_text(): lost=body(p,'protected void RememberCompletedHunt(').replace('protected ', '')+'\n'+lost
|
||||
retask=body(p,'protected void Retask(')
|
||||
queue=retask[retask.index(' ERR_HQHuntWindow hunt = Hunt(belief.BeliefId);'):]
|
||||
queue=queue[:queue.index(';',queue.index('if (hunt)'))+1]
|
||||
def adapt(code):
|
||||
code=code.replace('EngineerMine.', 'EngineerMine->')
|
||||
code=code.replace('ref ', '')
|
||||
code=code.replace('.IsEmpty()', '.empty()')
|
||||
code=code.replace('null','nullptr').replace('Math.','Math::').replace('ERR_GameMode.Get()', 'ERR_GameMode::Get()')
|
||||
code=code.replace('SCR_EntityHelper.DeleteEntityAndChildren', 'SCR_EntityHelper::DeleteEntityAndChildren')
|
||||
@@ -15,14 +24,14 @@ def adapt(code):
|
||||
code=code.replace('foreach (ERR_HQUnit other : m_Units)', 'for (ERR_HQUnit* other : m_Units)')
|
||||
code=re.sub(r'\b(mode|record|Group|unit|other|task|planned|hunt)\.', r'\1->', code)
|
||||
code=code.replace('mode->Forces().FindGroup', 'mode->Forces()->FindGroup')
|
||||
return code
|
||||
return code.replace('other->Task.', 'other->Task->')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
struct Math {static float Min(float a,float b){return std::min(a,b);}};
|
||||
struct ERR_HQTask {bool ReleaseOnly=false;int BeliefId=0;std::string WatchSourceId,SecuritySite;int WatchMessageId=0,WatchRequestId=0;bool Investigation(){return !WatchSourceId.empty();}};
|
||||
struct Math {static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}};
|
||||
struct ERR_HQTask {int OperationId=0,ExecutionId=0;float ExpiresAt=120000;bool ReleaseOnly=false;int BeliefId=0;std::string WatchSourceId,SecuritySite;int WatchMessageId=0,WatchRequestId=0;bool Investigation(){return !WatchSourceId.empty();}};
|
||||
struct ERR_HQReport {};
|
||||
struct ERR_HQRoute {};
|
||||
struct AIWaypoint {int deleted=0;};
|
||||
@@ -34,8 +43,10 @@ struct ERR_GameMode {Ledger ledger;static ERR_GameMode* current;
|
||||
static ERR_GameMode* Get(){return current;} Ledger* Forces(){return &ledger;}};
|
||||
ERR_GameMode* ERR_GameMode::current=nullptr;
|
||||
struct SCR_EntityHelper {static void DeleteEntityAndChildren(AIWaypoint* p){p->deleted++;}};
|
||||
struct ERR_HQHuntWindow {float NextHunt=0;};
|
||||
struct ERR_HQUnit {
|
||||
struct ERR_HQHuntWindow {float CompletedUntil=0;float NextHunt=0;};
|
||||
struct MineJob {int stops=0;void Stop(){stops++;}};
|
||||
struct TestWorld{operator bool(){return true;}float GetWorldTime(){return 0;}};struct TestGame{operator bool(){return true;}TestWorld GetWorld(){return {};}};TestGame GetGame(){return {};}struct ExecutionDouble{operator bool(){return false;}void Stop(float,bool){}void Receive(ERR_HQTask*,float){}};
|
||||
struct ERR_HQUnit {ExecutionDouble Execution;int Id=1;MineJob* EngineerMine=nullptr;
|
||||
SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
|
||||
ERR_HQTask *Task=nullptr,*PendingTask=nullptr;
|
||||
ERR_HQReport *Assignment=nullptr,*PendingOrder=nullptr;
|
||||
@@ -44,9 +55,9 @@ struct ERR_HQUnit {
|
||||
'''+adapt(unit)+r'''};
|
||||
struct Watch {int calls=0;std::string source;int message=0,request=0;
|
||||
bool FinishInvestigation(std::string s,int m,int r,float){calls++;source=s;message=m;request=r;return true;}};
|
||||
struct HQ {Watch m_Watch;std::vector<ERR_HQUnit*> m_Units;ERR_HQHuntWindow window;
|
||||
struct HQ {struct Operations{void Release(int,int,float){}}m_Operations;Watch m_Watch;std::vector<ERR_HQUnit*> m_Units;ERR_HQHuntWindow window;
|
||||
ERR_HQHuntWindow* Hunt(int){return &window;}
|
||||
'''+adapt(lost)+r'''};
|
||||
'''+adapt(lost)+'\nvoid QueueHorizon(ERR_HQTask* task,int id){struct Belief{int BeliefId;};Belief belief{id};'+adapt(queue)+'}'+r'''};
|
||||
int main(){
|
||||
ERR_GameMode mode;ERR_GameMode::current=&mode;ERR_ForceRecord record;mode.ledger.record=&record;
|
||||
SCR_AIGroup group;ERR_HQReport oldReport,newReport;ERR_HQTask oldTask,newTask;ERR_HQRoute route;
|
||||
@@ -61,7 +72,8 @@ int main(){
|
||||
assert(u.PlannedTask()==&oldTask);
|
||||
// Expiry of the current task must leave its replacement radio transaction intact.
|
||||
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
||||
u.ReleaseAssignment();
|
||||
MineJob miner;u.EngineerMine=&miner;
|
||||
u.ReleaseAssignment();assert(miner.stops==1);
|
||||
assert(!u.Assignment&&!u.Task&&!u.Waypoint);
|
||||
assert(u.PendingOrder==&newReport&&u.PendingTask==&newTask&&u.Downlink==&route);
|
||||
assert(oldWaypoint.deleted==1&&group.removals==1&&group.last==&oldWaypoint);
|
||||
@@ -82,9 +94,41 @@ int main(){
|
||||
hq.LostOrder(&u,1000);assert(hq.window.NextHunt==300000&&u.Task==&newTask&&u.Waypoint==&oldWaypoint);
|
||||
// Another group's pending hunt prevents duplicate retry commitments.
|
||||
u.Task=nullptr;u.Assignment=nullptr;u.Waypoint=nullptr;
|
||||
ERR_HQUnit peer;peer.PendingTask=&newTask;hq.m_Units.push_back(&peer);
|
||||
ERR_HQUnit peer;peer.PendingTask=&newTask;peer.PendingOrder=&newReport;hq.m_Units.push_back(&peer);
|
||||
u.PendingTask=&newTask;u.PendingOrder=&newReport;u.Downlink=&route;
|
||||
hq.LostOrder(&u,2000);assert(hq.window.NextHunt==300000);
|
||||
// Execute the actual Retask deadline extension, then fail its downlink.
|
||||
// The existing short delivered hunt must not inherit the failed long horizon.
|
||||
{HQ h;ERR_HQUnit owner;ERR_HQTask delivered,pending;ERR_HQReport r;ERR_HQRoute link;AIWaypoint w;
|
||||
delivered.BeliefId=7;delivered.ExpiresAt=120000;pending.BeliefId=7;pending.ExpiresAt=600000;
|
||||
owner.Task=&delivered;owner.Assignment=&r;owner.Waypoint=&w;
|
||||
owner.PendingTask=&pending;owner.PendingOrder=&r;owner.Downlink=&link;h.m_Units={&owner};h.window.NextHunt=300000;
|
||||
h.QueueHorizon(&pending,7);assert(h.window.NextHunt==780000);
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==300000);
|
||||
assert(owner.Task==&delivered&&owner.Assignment==&r&&owner.Waypoint==&w&&!owner.PendingTask);
|
||||
// A surviving peer's longer pending order remains fully reserved.
|
||||
ERR_HQUnit peer;ERR_HQTask active,replacement;
|
||||
active.BeliefId=7;active.ExpiresAt=400000;replacement.BeliefId=7;replacement.ExpiresAt=700000;
|
||||
peer.Task=&active;peer.Assignment=&r;peer.PendingTask=&replacement;peer.PendingOrder=&r;h.m_Units.push_back(&peer);
|
||||
owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==880000&&peer.PendingTask==&replacement&&peer.Task==&active);
|
||||
// An unrelated pending replacement cannot conceal its original same-track task.
|
||||
replacement.BeliefId=8;owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==580000);
|
||||
// Orphan pointers and non-pursuit orders cannot hold a hunt. A still-owned
|
||||
// expired active search preserves its completed interval.
|
||||
owner.Task=nullptr;owner.Assignment=nullptr;peer.Assignment=nullptr;peer.PendingOrder=nullptr;
|
||||
owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==45000);
|
||||
for(int kind=0;kind<4;kind++){
|
||||
peer.Assignment=&r;active.ExpiresAt=400000;active.ReleaseOnly=false;active.SecuritySite="";active.WatchSourceId="";
|
||||
if(kind==0){active.ReleaseOnly=true;}if(kind==1){active.SecuritySite="site";}if(kind==2){active.WatchSourceId="patrol";}if(kind==3){active.ExpiresAt=30000;}
|
||||
owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=1000000;
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==(kind==3?210000:45000));}
|
||||
// Clamping only: even a surviving long commitment must not raise earlier pacing.
|
||||
active.ExpiresAt=400000;owner.PendingTask=&pending;owner.PendingOrder=&r;h.window.NextHunt=40000;
|
||||
h.LostOrder(&owner,30000);assert(h.window.NextHunt==40000);
|
||||
}
|
||||
// A failed pending watch investigation completes only that pending lineage.
|
||||
ERR_HQTask activeWatch,pendingWatch;activeWatch.WatchSourceId="patrol-A";activeWatch.WatchMessageId=41;activeWatch.WatchRequestId=1;
|
||||
pendingWatch.WatchSourceId="patrol-B";pendingWatch.WatchMessageId=42;pendingWatch.WatchRequestId=2;
|
||||
@@ -107,4 +151,4 @@ int main(){
|
||||
hq.LostOrder(&u,2000);assert(hq.window.NextHunt==5000);
|
||||
}
|
||||
''')
|
||||
print('PASS: actual assignment cancellation/release preserves radio ownership, native waypoint lifecycle and failed-order retry boundaries')
|
||||
print('PASS: actual failed-retask deadline rollback plus assignment cancellation/release preserves radio ownership, native waypoint lifecycle and failed-order retry boundaries')
|
||||
|
||||
Reference in New Issue
Block a user