Open Eein opened 1 week ago
struct Controller {
reasoner_execution_context: Option<ReasonerExecutionContext>
}
struct ReasonerExecutionContext {
filters: Vec<ContextFilter>,
settings: ContextSettings,
}
// This can be generic
trait ContextFilter {
fn valid(action: Action, context: ReasonerExecutionContext) -> bool;
}
impl ContextFilter for PriorityTargetFilter {
fn valid(action: SoSAction, context: ReasonerExecutionContext) -> bool {
if context.priority_targets.len() > 0 {
let target = context.priority_targets.first()
return action.appraisal.target.guid == target.guid
}
true
}
}
impl ContextFilter for LiveManaFilter {
fn valid(action: SoSAction, context: ReasonerExecutionContext) -> bool {
!context.settings.hold_live_mana &&
actions.appraisal.player.live_mana >= action.appraisal.live_mana_cost
}
}
impl ContextFilter for ManaFilter {
fn valid(action: SoSAction, context: ReasonerExecutionContext) -> bool {
!context.settings.hold_mp &&
actions.appraisal.player.mp >= action.appraisal.mp_cost
}
}
impl ContextFilter for DeadFilter {
fn valid(action: SoSAction, context: ReasonerExecutionContext) -> bool {
!action.appraisal.player.is_dead
}
}
struct ContextSettings {
hold_live_mana: bool,
hold_mp: bool,
priority_targets: Vec<String> // combat enemy uuid or something
}
some pseudo code for filters maybe
Another thought:
I like generating all the possible actions, then filter, then maybe run a calculate
function to actually calculate scores for efficiency.
maybe something like:
let settings = controller.reasoner_execution_context.settings.clone()
actions.iter()
.filter(|r| reasoner.filters(r, settings))
.map(|action| action.calculate_score())
.first()
Doing it that way with the filters is one way. Another is to not generate things that are invalid moves (moving the "possibility" check to when creating the Appraisals
). I think we did it that way in the old TAS.
My thought is (and its probably short sighted), is that using filters like this instead of doing it in appraisal generation can help avoid contaminating the core utility ai stuff with assumptions about Sea of Stars things.
I'll try thinking through things from that angle as well...
Edit: or maybe both - where a skill/etc can set its own constraints.
Having a full list prior to filtering can be useful in debugging the list and assigning the efficacy of values.
Utility AI
The principal around this is to be able to describe these things in an clear and expandable way (hopefully generic)
In the majority of cases theres a few identifiers that prescribe the most value
questions:
appraisals: (base)
basic attack skills combos ultimates items
modifiers (multi)
damage modifiers damage modifiers override controller specific actions? (ChromaticApparation)
filters (remove)
live mana count dead status mp status hp status (redbar means heal, maybe in reasoner context) target (priority)
memory knowledge based modifiers (multi?)
will action prevent an enemy turn? will action delay an enemy turn? will damage kill enemy will damage end the fight will aoe damage kill enemy will aoe damage end the fight
questions:
fight specific modifiers
damage phases - some way to set in the controller than the enemy is taking increased damage?
fight specific mechanics
priority targets
questions:
Utility Helper
have live configurable values for utilty/reasoner execution context and a button to regenerate on the fly so these values can be tested at runtime
Enemy Improvements (Later)
Enemy Utility AI
Skill casting time (Later)
When deciding an action, we should account for the time it takes an action to complete. Its far better to collect this information during actual runs instead of manually timing things