Files
EscapistsReintroduced/tests/test_convoy_budget.py
T

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')