47 lines
2.6 KiB
Python
47 lines
2.6 KiB
Python
"""Exercise real PvE templates against 4.3.4 DBC rank, tree and dependency gates."""
|
|
from pathlib import Path
|
|
import json,subprocess,tempfile
|
|
root=Path(__file__).resolve().parents[2]
|
|
data=json.loads((Path(__file__).parent/'fixtures/cata_pve_talents.json').read_text())
|
|
rows=','.join('{'+k+',{'+','.join(map(str,v))+'}}' for k,v in data['talents'].items())
|
|
tabs=','.join('{'+k+',{'+','.join(map(str,v))+'}}' for k,v in data['tabs'].items())
|
|
code=r'''
|
|
#include "AdventureTalentPolicy.h"
|
|
#include "AdventureRotationPolicy.h"
|
|
#include <map>
|
|
#include <cassert>
|
|
struct Talent{unsigned tab,tier,ranks,dependency,dependencyRank;};
|
|
struct Tab{unsigned mask,order;};
|
|
std::map<unsigned,Talent> talents={ROWS};std::map<unsigned,Tab> tabs={TABS};
|
|
int main(){
|
|
for(unsigned klass:{1,2,3,4,5,6,7,8,9,11})for(unsigned spec=0;spec<3;++spec)for(bool tank:{false,true}){
|
|
auto build=AdventureTalentPolicy::Build(klass,spec,tank);if(build.empty())continue;
|
|
unsigned primary=0;for(auto const& t:tabs)if(t.second.mask==(1u<<(klass-1)) && t.second.order==spec)primary=t.first;
|
|
assert(primary);unsigned total=0;
|
|
for(auto t:build){assert(talents.count(t.Id));auto d=talents.at(t.Id);assert(t.Ranks<=d.ranks);assert(tabs.at(d.tab).mask==(1u<<(klass-1)));total+=t.Ranks;}
|
|
assert(total==41);
|
|
for(unsigned budget=1;budget<=41;++budget){
|
|
std::map<unsigned,unsigned> known;unsigned left=budget;
|
|
auto learn=[&](unsigned id,unsigned rank){
|
|
auto d=talents.at(id);if(known[id]>rank || rank>=d.ranks)return false;
|
|
auto cost=rank+1-known[id];if(cost>left)return false;
|
|
if(d.dependency && known[d.dependency]<=d.dependencyRank)return false;
|
|
unsigned spent=0,main=0;for(auto p:known){if(talents.at(p.first).tab==d.tab)spent+=p.second;if(talents.at(p.first).tab==primary)main+=p.second;}
|
|
if(spent<d.tier*5 || (d.tab!=primary && main<31))return false;
|
|
known[id]=rank+1;left-=cost;return true;
|
|
};
|
|
assert(AdventureTalentPolicy::Spend(build,learn,[&]{return left;}));assert(!left);
|
|
if(budget==41)for(auto t:build)assert(known[t.Id]==t.Ranks);
|
|
}
|
|
}
|
|
for(unsigned klass:{1,2,3,4,5,6,7,8,9,11})for(unsigned seed=0;seed<100;++seed){
|
|
auto spec=AdventureRotationPolicy::SoloSpec(klass,seed);assert(!AdventureTalentPolicy::Build(klass,spec).empty());
|
|
}
|
|
}
|
|
'''.replace('ROWS',rows).replace('TABS',tabs)
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p=Path(d);(p/'test.cpp').write_text(code)
|
|
subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
|
|
subprocess.run([str(p/'test')],check=True)
|
|
print('Canonical Cata PvE builds: 4.3.4 class/tree/rank/prerequisite gates, all 1..41 point budgets, solo selection coverage passed')
|