51 lines
5.2 KiB
Python
51 lines
5.2 KiB
Python
"""Run actual canonical convoy receipt and shared ledger-cap methods."""
|
|
import os,re
|
|
from pathlib import Path
|
|
from policy_harness import ROOT,run
|
|
p=Path(os.environ.get('ERR_CONVOY_BUDGET',str(ROOT/'addons/ai/Scripts/Game/ERR/ERR_ConvoyBudget.c')))
|
|
s=p.read_text();s=re.sub(r'//[^\n]*','',s).replace('protected ','').replace('ref ','').replace('class ','struct ')
|
|
s=s.replace('array<int>','Array<int>').replace('array<ERR_ConvoyReceipt>','Array<ERR_ConvoyReceipt*>')
|
|
for typ in ['ERR_GameMode','ERR_ForceLedger','ERR_ForceRecord','ERR_ConvoyReceipt']:
|
|
s=re.sub(r'\b'+typ+r' ([A-Za-z_]\w*)',typ+r'* \1',s)
|
|
for obj in ['m_Mode','ledger','record','receipt','known']:s=s.replace(obj+'.',obj+'->')
|
|
for cls in ['Math','Replication','ERR_GameMode','ERR_ForceKind']:s=s.replace(cls+'.',cls+'::')
|
|
s=s.replace('Forces().','Forces()->').replace('foreach (','for (').replace('null','nullptr').replace('void ERR_ConvoyPool(','ERR_ConvoyPool(').replace('vehicle.ToString()','std::to_string(vehicle)').replace('\n}\n','\n};\n')
|
|
s=re.sub(r'\b(bool|int|[A-Za-z_]\w*\*) ([A-Za-z_]\w*);',r'\1 \2 {};',s)
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <algorithm>
|
|
struct string:std::string{using std::string::string;string(std::string s):std::string(s){}bool IsEmpty(){return empty();}int Length(){return size();}};
|
|
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;bool Contains(T v){return std::find(this->begin(),this->end(),v)!=this->end();}void Insert(T v){this->push_back(v);}int Count(){return this->size();}};
|
|
namespace Math{int Clamp(int a,int low,int high){return std::clamp(a,low,high);}}
|
|
namespace Replication{bool server=true;bool IsServer(){return server;}}
|
|
enum class ERR_ForceKind{GARRISON,CONVOY};
|
|
struct ERR_ForceRecord{int Id=0,InitialStrength=0,Strength=0;bool Physical=true,Eliminated=false;void* Group=this;ERR_ForceKind Kind=ERR_ForceKind::CONVOY;string SourceId;};
|
|
struct ERR_ForceLedger{Array<ERR_ForceRecord*>records;int Count(){return records.Count();}ERR_ForceRecord* At(int i){return records[i];}ERR_ForceRecord* Find(int id){for(auto* r:records){if(r->Id==id)return r;}return nullptr;}};
|
|
struct ERR_GameMode{static inline ERR_GameMode* current=nullptr;ERR_ForceLedger ledger;bool active=true;static ERR_GameMode* Get(){return current;}bool Active(){return active;}ERR_ForceLedger* Forces(){return &ledger;}};
|
|
''' + s + r'''
|
|
int main(){
|
|
ERR_GameMode mode;ERR_GameMode::current=&mode;ERR_ForceRecord guard;guard.Id=1;guard.Strength=41;mode.ledger.records.Insert(&guard);
|
|
ERR_ConvoyPool pool(&mode,3);auto receipt=pool.Reserve("road-a");assert(receipt&&pool.CommittedBodies()==60);assert(pool.Reserve("road-a")==receipt);assert(!pool.Reserve("road-b"));
|
|
ERR_ConvoyReceipt forged;assert(!pool.Consume(&forged));assert(pool.Consume(receipt)&&!pool.Consume(receipt));
|
|
for(int i=0;i<6;i++){
|
|
auto* r=new ERR_ForceRecord();r->Id=i+2;r->InitialStrength=ERR_ConvoyPool::CrewCount(i);r->Strength=r->InitialStrength;r->SourceId="road-a-vehicle-"+std::to_string(i);mode.ledger.records.Insert(r);
|
|
assert(pool.BindRecord(receipt,i,r));assert(pool.BindRecord(receipt,i,r));assert(pool.CommittedBodies()==60);
|
|
}
|
|
assert(receipt->OutstandingBodies==0&&receipt->OutstandingGroups==0);assert(!pool.Consume(receipt));
|
|
// Dead/captured/released forces never refund a consumed receipt's convoy slot.
|
|
guard.Strength=0;guard.Eliminated=true;auto second=pool.Reserve("road-b");assert(second);assert(pool.Consume(second));assert(!pool.Consume(receipt));
|
|
ERR_GameMode other;ERR_GameMode::current=&other;assert(!pool.Reserve("wrong-world")&&!pool.Consume(second));ERR_GameMode::current=&mode;
|
|
ERR_GameMode crowded;ERR_GameMode::current=&crowded;ERR_ForceRecord many;many.Strength=42;crowded.ledger.records.Insert(&many);ERR_ConvoyPool limited(&crowded,8);assert(!limited.Reserve("too-many"));
|
|
many.Strength=41;auto reserved=limited.Reserve("exact");assert(reserved);many.Strength=42;assert(!limited.Consume(reserved));many.Strength=41;assert(limited.Consume(reserved));
|
|
ERR_ForceRecord invalid;invalid.Id=2;invalid.InitialStrength=2;invalid.Strength=2;invalid.SourceId="exact-vehicle-0";
|
|
assert(!limited.BindRecord(reserved,0,&invalid));crowded.ledger.records.Insert(&invalid);invalid.Kind=ERR_ForceKind::GARRISON;assert(!limited.BindRecord(reserved,0,&invalid));invalid.Kind=ERR_ForceKind::CONVOY;
|
|
invalid.Strength=1;assert(!limited.BindRecord(reserved,0,&invalid));invalid.Strength=2;invalid.Physical=false;assert(!limited.BindRecord(reserved,0,&invalid));invalid.Physical=true;assert(limited.BindRecord(reserved,0,&invalid));
|
|
Replication::server=false;assert(!limited.Reserve("client"));Replication::server=true;
|
|
ERR_GameMode formations;ERR_GameMode::current=&formations;for(int i=0;i<123;i++){auto* r=new ERR_ForceRecord();r->Strength=0;r->Physical=false;formations.ledger.records.Insert(r);}ERR_ConvoyPool capped(&formations,8);assert(!capped.Reserve("formation-cap"));
|
|
ERR_GameMode finite;ERR_GameMode::current=&finite;ERR_ConvoyPool one(&finite,1);assert(one.Reserve("first"));assert(!one.Reserve("second"));
|
|
}
|
|
''')
|
|
print('PASS: actual convoy receipt identity/one-use, shared60-body and128-formation caps, outstanding/native-ledger accounting, exact19-body composition, source reuse, mode/client guards and no consumed receipt refunds')
|