Fix progression stalls and add normal class trainer visits
Core tests / core (push) Successful in 29s
Core tests / core (push) Successful in 29s
This commit is contained in:
@@ -8,7 +8,7 @@ test('inference preserves intervening observations and cancels after retirement'
|
||||
const now=Date.now();
|
||||
const bot={id:'hero',name:'Zaku',status:'online',tier:'hero',decisions:0,
|
||||
personality:{activities:['explore']},autonomy:{managed:true},level:5,zone:'Old zone'};
|
||||
let state={revision:1,bots:[bot],events:[],sequence:0,settings:{}};
|
||||
let state={revision:1,bots:[bot],events:[],sequence:0,settings:{hardcore:{enabled:false}}};
|
||||
const store={load:async()=>structuredClone(state),save:async(s,revision)=>{
|
||||
assert.equal(revision,state.revision);state=s;
|
||||
}};
|
||||
@@ -29,3 +29,9 @@ test('inference preserves intervening observations and cancels after retirement'
|
||||
assert.equal((await runLiveAutonomyTick(store,{}, {},{now,forceBotId:'hero'})).ran,false);
|
||||
assert.equal(queued,1);
|
||||
});
|
||||
|
||||
test('rest requests never enable indefinite stay',()=>{
|
||||
const {compileLivePlayerbotIntent}=require('../.test-build/autonomy.js');
|
||||
const source=compileLivePlayerbotIntent('A',{action:'rest',goal:'Recover',reason:'Tired'});
|
||||
assert.doesNotMatch(source,/\+stay/);assert.match(source,/-stay/);assert.match(source,/GetHealthPct/);assert.match(source,/AzerothAITrainingOverride/);
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ function make(id,name)
|
||||
local p={id=id,name=name,dist=20}
|
||||
function p:GetName()return self.name end;function p:GetGUIDLow()return self.id end;function p:GetAccountId()return 10+self.id end
|
||||
function p:IsPlayerbot()return not self.human end;function p:IsDead()return self.dead end;function p:IsInCombat()return self.combat end
|
||||
function p:IsCasting()return false end;function p:IsTaxi()return false end
|
||||
function p:IsCasting()return false end;function p:HasUnitState(state)assert(state==0x00000100);return false end
|
||||
function p:GetBotAI()return {HasGameClientMaster=function()return self.controlled end}end
|
||||
function p:GetTeam()return self.team or 0 end;function p:GetLevel()return 5 end;function p:GetMapId()return 0 end;function p:GetPhaseMask()return 1 end
|
||||
function p:GetDistance()return self.dist end;function p:GetGroup()return self.group end
|
||||
|
||||
@@ -9,7 +9,7 @@ function CreateLuaEvent(fn)tick=fn end;function PrintError(s)error(s)end
|
||||
p={ai={}};function GetPlayersInWorld()return {p}end
|
||||
function p:GetGUIDLow()return 1 end;function p:GetAccountId()return 12 end;function p:GetName()return 'A' end
|
||||
function p:IsPlayerbot()return true end;function p:GetBotAI()return self.ai end;function p.ai:HasGameClientMaster()return p.human end
|
||||
function p:IsDead()return p.dead end;function p:IsInCombat()return p.combat end;function p:IsCasting()return false end;function p:IsTaxi()return false end
|
||||
function p:IsDead()return p.dead end;function p:IsInCombat()return p.combat end;function p:IsCasting()return false end;function p:HasUnitState(state)assert(state==0x00000100);return false end
|
||||
function p:GetGroup()return p.group end;function p:GetFreeTalentPoints()return 1 end
|
||||
function p:GetItemByPos(_,slot)if slot==15 then return {IsBroken=function()return true end,GetUInt32Value=function(_,i)return i==0x003D and 50 or 0 end}end end
|
||||
function p.ai:GetInventoryItems()return {{GetEntry=function()return 10 end,GetCount=function()return 3 end}}end
|
||||
|
||||
@@ -28,3 +28,9 @@ test('monitor distinguishes mana recovery, named leader and ghost state',()=>{
|
||||
b.world.survivalAction='regroup requested';b.world.leaderName='Zaku';assert.equal(activityReason(b,now).label,'Catching up to Zaku');
|
||||
b.world.isGhost=true;assert.equal(activityReason(b,now).label,'Recovering as a ghost');
|
||||
});
|
||||
|
||||
test('trainer activity reports pending work and verified learning separately',()=>{
|
||||
const b=bot();Object.assign(b.world,{trainingAt:now,trainingLevel:8,trainingPending:3,trainingLearned:2,trainingTrainer:'Teacher',trainingStatus:'traveling'});
|
||||
const a=activityReason(b,now);assert.equal(a.label,'Visiting class trainer');assert.match(a.detail,/3 identified spells pending, 2 verified trainer purchases/);
|
||||
b.world.inCombat=true;assert.equal(activityReason(b,now).label,'In combat');
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ function player:IsDead()return self.dead end
|
||||
function player:IsInCombat()return self.combat end
|
||||
function player:IsCasting()return self.casting end
|
||||
function player:StopSpellCast()self.casting=false;self.interrupted=true end
|
||||
function player:IsTaxi()return self.taxi end
|
||||
function player:HasUnitState(state)assert(state==0x00000100);return self.taxi end
|
||||
function player:IsRooted()return self.root end
|
||||
function player:GetGroup()return self.group end
|
||||
function player:GetX()return self.x end
|
||||
@@ -58,6 +58,7 @@ l=new();l.execute('step(0);now=now+601;tick();assert(attempts==0)')
|
||||
print('Intent, movement, health progress, active identity and stale input guards passed')
|
||||
l=new();l.execute('player.group={GetMembers=function()return {{IsPlayerbot=function()return false end}} end,GetMembersCount=function()return 1 end};step(0);step(601);assert(attempts==0)')
|
||||
l=new();l.execute('player.motion=7;step(0);step(601);assert(attempts==1 and expired==1)')
|
||||
l=new();l.execute('function player:IsTaxi()return false end;player.taxi=true;player.motion=7;step(0);step(601);assert(attempts==0 and expired==0,"In-flight state protects a player even though IsTaxi NPC predicate is false")')
|
||||
print('Human group exclusion and stale flight recovery passed')
|
||||
|
||||
l=new();l.execute("step(0);step(600);player.x=8;step(30);assert(string.find(captured[#captured], '726573756D6564'))")
|
||||
|
||||
@@ -15,7 +15,7 @@ function make(id,name)
|
||||
function p:GetGUIDLow()return self.id end;function p:GetName()return self.name end;function p:GetAccountId()return 10+self.id end
|
||||
function p:IsPlayerbot()return not self.human end;function p:IsDead()return self.dead end;function p:GetBotAI()return self.ai end
|
||||
function p:GetPowerType()return self.powerType or 1 end;function p:GetPowerPct()return self.mana or 100 end;function p:GetAttackers()return self.attackers or {} end;function p:GetHealthPct()return self.hp end;function p:IsInCombat()return self.combat end
|
||||
function p:IsTaxi()return self.taxi end;function p:IsCasting()return self.cast end;function p:IsRooted()return false end
|
||||
function p:HasUnitState(state)assert(state==0x00000100);return self.taxi end;function p:IsCasting()return self.cast end;function p:IsRooted()return false end
|
||||
function p:GetX()return self.x or 0 end;function p:GetY()return 0 end
|
||||
function p:GetUnfriendlyUnitsInRange()return self.friendly and {} or self.selected and {self.selected} or {} end;function p:GetSelection()return self.selected end;function p:StopSpellCast()self.cast=false;self.interrupted=true end
|
||||
function p:AttackStop()self.attackStopped=true end;function p:MoveStop()self.waited=true end
|
||||
@@ -65,3 +65,7 @@ l=new();l.execute("group();A.distance=50;B.distance=50;A.strategies={rpg=true,gr
|
||||
l=new();l.execute("group();B.distance=50;B.combat=true;B.hp=20;step(0);assert(calls[1]=='flee','Survival outranks regrouping');assert(not B.master)")
|
||||
l=new();l.execute("group();A.distance=40;step(0);A.waited=false;step(22);assert(not A.waited,'Leader waiting must remain bounded');A.distance=5;step(2);A.distance=40;step(2);assert(A.waited,'A fresh separation starts a fresh wait')")
|
||||
print('Cohesion suppresses competing work, keeps catch-up hysteresis, yields to danger, and resets bounded waits')
|
||||
|
||||
l=new();l.execute("A.strategies={stay=true};step(0);assert(not A.strategies.stay,'Fully recovered autonomous resident must not remain in stay')")
|
||||
l=new();l.execute("group();B.distance=50;step(0);step(40);assert(B.master==nil,'No-progress regroup must release its owned master');assert(not AzerothAISurvivalOverride(B));step(30);assert(B.master==nil,'Backoff must prevent immediate retry')")
|
||||
print('Recovered stay cleared and non-progressing regroup bounded')
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
from pathlib import Path
|
||||
from lupa import LuaRuntime
|
||||
source=(Path(__file__).resolve().parents[1]/'ale/azerothai_training.lua').read_text()
|
||||
setup='''
|
||||
now=1000;os.time=function()return now end;calls={};moves=0;learned=0;team=2;callbacks={}
|
||||
function AuthDBExecute()end
|
||||
function AuthDBQuery(s)
|
||||
if s:find("state='active'",1,true) then return {GetUInt32=function(_,i)return i==0 and 1 or 11 end,GetString=function()return 'A' end,NextRow=function()return false end} end
|
||||
if s:find('SELECT flags',1,true) then return {GetString=function()return 'new rpg,quest' end}end
|
||||
end
|
||||
function WorldDBQuery()return {GetUInt32=function(_,i)return ({[0]=8,[1]=100,[2]=0,[7]=team})[i]end,GetFloat=function(_,i)return ({[3]=10,[4]=20,[5]=30})[i]end,GetString=function()return 'Trainer' end,NextRow=function()return false end}end
|
||||
function CreateLuaEvent(f)tick=f end;function PrintError(s)error(s)end
|
||||
p={ai={},money=100,level=4,distance=0,pending=2}
|
||||
function p:GetGUIDLow()return 1 end;function p:GetAccountId()return 11 end;function p:GetName()return 'A' end
|
||||
function p:GetLevel()return self.level end;function p:GetClass()return 8 end;function p:GetTeam()return 0 end;function p:GetMapId()return 0 end
|
||||
function p:GetX()return 10 end;function p:GetY()return 20 end;function p:IsPlayerbot()return true end;function p:GetBotAI()return self.ai end
|
||||
function p.ai:HasGameClientMaster()return p.human end;function p:IsDead()return p.dead end;function p:IsInCombat()return p.combat end
|
||||
function p:IsCasting()return false end;function p:HasUnitState(state)assert(state==0x00000100);return self.taxi end;function p:GetGroup()return p.group end;function p:GetCoinage()return self.money end
|
||||
function p.ai:GetClassTrainerStatus()return p.pending,10,20 end
|
||||
function p.ai:TrainAtClassTrainer()assert(p.distance<=5);local n=p.pending;learned=learned+n;p.pending=0;p.money=p.money-20;return n end
|
||||
function p.ai:HasStrategy()return true end;function p.ai:ChangeStrategy(s)table.insert(calls,s)end
|
||||
function p:GetNearestCreature()return {} end;function p:GetDistance()return self.distance end;function p:MoveStop()end;function p:MoveTo()moves=moves+1 end
|
||||
function GetPlayersInWorld()return {p}end
|
||||
function AzerothAISurvivalOverride()return p.recovering end
|
||||
'''
|
||||
def new(extra=''):
|
||||
l=LuaRuntime();l.execute(setup+extra);l.execute(source);return l
|
||||
l=new();l.execute("tick();assert(learned==2 and p.money==80);assert(AzerothAITrainingSnapshot(p).trainingStatus=='no new trainable spells');now=1010;p.level=5;p.pending=1;tick();assert(learned==3,'New level is checked without waiting a minute')")
|
||||
for flag in ['human','dead','combat','recovering','taxi']:
|
||||
l=new();l.execute(f'p.{flag}=true;tick();assert(learned==0 and moves==0)')
|
||||
l=new();l.execute("p.money=0;tick();assert(learned==0 and moves==0 and AzerothAITrainingSnapshot(p).trainingStatus=='saving for training')")
|
||||
l=new();l.execute("p.distance=20;tick();assert(learned==0 and moves==1 and AzerothAITrainingOverride(p));now=1060;tick();assert(not AzerothAITrainingOverride(p),'Stalled visit releases held work')")
|
||||
l=new('team=4');l.execute("tick();assert(learned==0 and moves==0,'Opposing faction trainer excluded')")
|
||||
l=new();l.execute("p.distance=20;tick();assert(AzerothAITrainingOverride(p));p.human=true;now=1010;tick();assert(not AzerothAITrainingOverride(p) and moves==1,'Human takeover releases our lease without continuing travel')")
|
||||
l=new();l.execute("p.group={IsRaidGroup=function()return false end,GetMembersCount=function()return 2 end,GetMembers=function()return {p}end};tick();assert(learned==0 and moves==0)")
|
||||
print('Trainer checks: every level, verified learning, money, interaction distance, stalled-path release and human/combat/death/group/faction guards passed')
|
||||
Reference in New Issue
Block a user