Integrate delivered HQ memory, finite physical convoy and RHS WCS dependencies
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
"""Execute actual HQ assignment ownership and failed-radio backoff bodies."""
|
||||
from policy_harness import ROOT, body, run
|
||||
import re
|
||||
p=ROOT/'addons/ai/Scripts/Game/ERR/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 ', '')
|
||||
def adapt(code):
|
||||
code=code.replace('null','nullptr').replace('Math.','Math::').replace('ERR_GameMode.Get()', 'ERR_GameMode::Get()')
|
||||
code=code.replace('SCR_EntityHelper.DeleteEntityAndChildren', 'SCR_EntityHelper::DeleteEntityAndChildren')
|
||||
code=code.replace('ERR_HQUnit unit', 'ERR_HQUnit* unit')
|
||||
code=code.replace('ERR_HQTask ', 'ERR_HQTask* ').replace('ERR_GameMode ', 'ERR_GameMode* ')
|
||||
code=code.replace('ERR_ForceRecord record;', 'ERR_ForceRecord* record=nullptr;')
|
||||
code=code.replace('ERR_HQHuntWindow hunt', 'ERR_HQHuntWindow* hunt')
|
||||
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
|
||||
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;int WatchMessageId=0,WatchRequestId=0;bool Investigation(){return !WatchSourceId.empty();}};
|
||||
struct ERR_HQReport {};
|
||||
struct ERR_HQRoute {};
|
||||
struct AIWaypoint {int deleted=0;};
|
||||
struct SCR_AIGroup {int removals=0; AIWaypoint* last=nullptr;
|
||||
void RemoveWaypoint(AIWaypoint* p){removals++;last=p;}};
|
||||
struct ERR_ForceRecord {bool Eliminated=false;int Duty=2,HomeDuty=1,DutyTarget=200,HomePosition=100;};
|
||||
struct Ledger {ERR_ForceRecord* record=nullptr;ERR_ForceRecord* FindGroup(SCR_AIGroup*){return record;}};
|
||||
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 {
|
||||
SCR_AIGroup* Group=nullptr;AIWaypoint* Waypoint=nullptr;
|
||||
ERR_HQTask *Task=nullptr,*PendingTask=nullptr;
|
||||
ERR_HQReport *Assignment=nullptr,*PendingOrder=nullptr;
|
||||
ERR_HQRoute* Downlink=nullptr;bool PendingCheckin=false;
|
||||
'''+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;
|
||||
ERR_HQHuntWindow* Hunt(int){return &window;}
|
||||
'''+adapt(lost)+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;
|
||||
oldTask.BeliefId=1;newTask.BeliefId=2;AIWaypoint oldWaypoint;
|
||||
ERR_HQUnit u;u.Group=&group;u.Waypoint=&oldWaypoint;u.Assignment=&oldReport;u.Task=&oldTask;
|
||||
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
||||
assert(u.PlannedTask()==&newTask);
|
||||
u.CancelPending();
|
||||
assert(!u.PendingOrder&&!u.PendingTask&&!u.Downlink);
|
||||
assert(u.Task==&oldTask&&u.Assignment==&oldReport&&u.Waypoint==&oldWaypoint);
|
||||
assert(oldWaypoint.deleted==0&&group.removals==0&&record.Duty==2);
|
||||
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();
|
||||
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);
|
||||
assert(record.Duty==record.HomeDuty&&record.DutyTarget==record.HomePosition);
|
||||
u.ReleaseAssignment();assert(oldWaypoint.deleted==1&&group.removals==1);
|
||||
u.Release();assert(!u.PendingOrder&&!u.PendingTask&&!u.Downlink);
|
||||
// Elimination and absent group/mode cannot rewrite casualty state or double-delete.
|
||||
AIWaypoint detached;u.Waypoint=&detached;u.Group=nullptr;record.Eliminated=true;record.Duty=9;
|
||||
ERR_GameMode::current=nullptr;u.Release();assert(detached.deleted==1&&record.Duty==9);
|
||||
ERR_GameMode::current=&mode;u.Group=&group;u.ReleaseAssignment();assert(record.Duty==9);
|
||||
// A failed initial dispatch can retry after a short backoff.
|
||||
HQ hq;hq.m_Units={&u};hq.window.NextHunt=300000;
|
||||
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;
|
||||
hq.LostOrder(&u,1000);assert(hq.window.NextHunt==16000&&!u.PendingOrder);
|
||||
// Failed retask preserves delivered assignment and its active hunt window.
|
||||
u.Task=&newTask;u.Assignment=&oldReport;u.Waypoint=&oldWaypoint;
|
||||
u.PendingOrder=&newReport;u.PendingTask=&newTask;u.Downlink=&route;hq.window.NextHunt=300000;
|
||||
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);
|
||||
u.PendingTask=&newTask;u.PendingOrder=&newReport;u.Downlink=&route;
|
||||
hq.LostOrder(&u,2000);assert(hq.window.NextHunt==300000);
|
||||
// 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;
|
||||
u.Task=&activeWatch;u.Waypoint=&oldWaypoint;u.Assignment=&oldReport;
|
||||
u.PendingTask=&pendingWatch;u.PendingOrder=&newReport;u.Downlink=&route;
|
||||
hq.LostOrder(&u,3000);assert(hq.m_Watch.calls==1&&hq.m_Watch.source=="patrol-B"&&hq.m_Watch.message==42&&hq.m_Watch.request==2);
|
||||
assert(u.Task==&activeWatch&&u.Waypoint==&oldWaypoint&&u.Assignment==&oldReport);
|
||||
assert(!u.PendingTask&&!u.PendingOrder&&!u.Downlink&&hq.window.NextHunt==300000);
|
||||
// Dropped status-check-in transport cannot finish the delivered investigation.
|
||||
u.PendingCheckin=true;u.Downlink=&route;hq.LostOrder(&u,4000);
|
||||
assert(!u.PendingCheckin&&!u.Downlink&&u.Task==&activeWatch&&hq.m_Watch.calls==1);
|
||||
ERR_HQTask recall;recall.ReleaseOnly=true;u.PendingTask=&recall;u.PendingOrder=&newReport;u.Downlink=&route;
|
||||
hq.LostOrder(&u,5000);assert(!u.PendingTask&&u.Task==&activeWatch&&u.Waypoint==&oldWaypoint&&hq.m_Watch.calls==1);
|
||||
u.Task=nullptr;u.Waypoint=nullptr;u.Assignment=nullptr;
|
||||
// Unrelated belief cannot suppress bounded retry, and existing sooner retry remains sooner.
|
||||
peer.PendingTask=&oldTask;u.PendingTask=&newTask;hq.window.NextHunt=5000;
|
||||
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')
|
||||
Reference in New Issue
Block a user