#big-brain

501 messages · Page 1 of 1 (latest)

silk marlin
#

https://codeberg.org/zkat/big-brain

For all of your utility AI needs. Maintained by @pliant crag!

big-brain is a Utility AI library for games, built for the Bevy Game Engine

It lets you define complex, intricate AI behaviors for your entities based on their perception of the world. Definitions are heavily data-driven, using plain Rust, and you only need to program Scorers (entities that look at your game world and come up with a Score), and Actions (entities that perform actual behaviors upon the world). No other code is needed for actual AI behavior.

See the documentation for more details.

Features
Highly concurrent/parallelizable evaluation.
Integrates smoothly with Bevy.
Proven game AI model.
Highly composable and reusable.
State machine-style continuous actions/behaviors.
Action cancellation.

pliant crag
#

Big Brain 0.19.0 is out: #crates message

trail raft
trail raft
#

Also a question about this example:

pub fn thirst_system(
    time: Res<Time>,
    mut thirsts: Query<(Entity, &mut Thirst)>,
    // We need to get to the Thinker. That takes a couple of steps.
    has_thinkers: Query<&HasThinker>,
    mut thinkers: Query<(&mut Thinker, &ActionSpan)>,
) {
    for (actor, mut thirst) in &mut thirsts {
        thirst.thirst += thirst.per_second * (time.delta().as_micros() as f32 / 1_000_000.0);
        if thirst.thirst >= 100.0 {
            let thinker_ent = has_thinkers.get(actor).unwrap().entity();

Why not just mut thirsts: Query<(Entity, &HasThinker, &mut Thirst)>?

pliant crag
#

That would probably be better yes

slate sparrow
#

@pliant crag hey I’ve been developing pddl_rs crate for a while - it’s a classical plan solver. I’m still working on it to allow for solving more complex plans but do you have any needs for such a tool?

#

Hitachical task networks (which I hope to support later too) are an extension to classical planning

pliant crag
#

Big-brain isn't really a planner, afaict. It's a different way to handle decision-making altogether.

visual phoenix
#

@pliant crag How would you implement a simple "Wait for 30 seconds" action, where would I track the 30 seconds, in the scorer right? Not the actual Wait action that does nothing?

pliant crag
visual phoenix
pliant crag
#

No, your action can just keep spinning until the 30s have passed

#

This isn’t the Scorer’s job

visual phoenix
#

instead that what the Scorer does

pliant crag
#

You make your WaitAction complete on Canceled

#

That way, when a Scorer overrides it, it’ll end

#

So the Action itself says “ok I’ll wait 30s to do the next thing”, and the Scorer related to it determines whether it finishes early or continues to completion

visual phoenix
#

Right so does that Scorer still need something like this:

    info!("Waiting at destination");
    score.set(0.9);
} else {
    info!("Done waiting at destination");
    score.set(0.0);
}```
pliant crag
#

You could also force the 30s to pass by ignoring the cancellation

#

It depends on why you want to wait

visual phoenix
#

Sorry remind me how the scorer cancels or waits for the action to complete?

pliant crag
#

If another scorer gets picked, the current Action is canceled

visual phoenix
#

So I can block for 30 seconds if I ignore the cancellation?

pliant crag
#

Yes

#

But that means a hard block on any other actions happening

#

A Wait that accepts cancellation I guess would mainly be useful when used in composite actions like Steps

#

So you can say “walk here. Wait 30s. Do something else “, but all triggered off a single condition

visual phoenix
#

Right, a Wait for 30 seconds but do flee if an enemy comes nearby would have to handled at the Scorer level no?

pliant crag
#

Wait for 30s to do what?

#

If you want to wait 30s to do another action, you would do this not at the Scorer level, but at the Thinker level

#
Thinker::new()
 .when(
  SomeScorer,
  Steps::new().
    .step(WaitAction(Duration::from_s(30))
    .step(Yell(“all clear!”.into())))
visual phoenix
# pliant crag If you want to wait 30s to do another action, you would do this not at the Score...

Right what about in the scenario where an entity has reached their destination and the MoveTo action just returns *state = ActionState::Success;

Is it a problem for the Scorer to just continuously spam:

2024-03-15T22:02:05.994430Z DEBUG action{ent=7v0 label="Tax Collector Ship"}:thinker{actor=5v0}:scorer{ent=9v0 label="TaxesToCollect"}: big_brain::thinker: Winning scorer chosen with score 1
2024-03-15T22:02:05.994526Z DEBUG action{ent=7v0 label="Tax Collector Ship"}:thinker{actor=5v0}: big_brain::thinker: Spawning next action
2024-03-15T22:02:05.994656Z DEBUG action{ent=7v0 label="Tax Collector Ship"}:thinker{actor=5v0}:action{ent=16v78 label="MoveToPos"}: big_brain::actions: New Action spawned.```
#

Should I add an MoveTo then Idle step?

#

Or does it matter that MoveTo is executing over and over again and do nothing basically

visual phoenix
#

I just added an Idle step action after every MoveTo and it definitely helps me preventing the action spam in the debug logs

pliant crag
#

It makes sense to not fire a MoveTo unless you actually need to move to a destination yes

kind flame
#

hi @pliant crag, i'm liking the library so far! this is my first exposure to utility AI. I am having an issue where some of my actors start stalling out. In my action system, I see the log message partition path state is success (line 79), but i don't ever see the generate path action successful (line 92). Any pointers on where to look, for why it would never get to the ActionState:Success branch?

pliant crag
#

If you want to run code on success or failure, move it to a separate match below that runs right after you set the state

kind flame
#

ah, so it's not gauranteed that one of those two branches will be executed? success/failure

pliant crag
#

If I recall, it’s guaranteed they never will

#

Because the Thinker will end the action when it sees those states are set

kind flame
#

so if i have cleanup, I should do that in the executing or cancelled branches?

pliant crag
#

Or in another match, right below this one

#

Match/if

kind flame
pliant crag
#

@kind flame it might help to use big-brain's built-in logging capabilities

#

(possibly even with trace to get as much detail as possible)

#

that should tell you more about how big brain is making decisions.

plain pewter
#

Hi @pliant crag 👋 im working on a distributed ai library written in bevy. The goal is to coordinate multiple ai layers working together, if you're interested I'd love to hear your thoughts 🙂 no worries otherwise, all the best
https://mrchantey.github.io/beet/

pliant crag
plain pewter
pliant crag
#

I don’t really want to collaborate with any project doing LLMs, unfortunately

#

Just on ethical grounds

plain pewter
last panther
#

@pliant crag thanks for this great crate so far. I've been using it for some mob behavior (find target, follow path to target, walk randomly, attack, etc).
There is one limitation Im facing: because the thinker is a component without generics you can only have one unit of thinking per entity. I do feel sometimes that entities can perform different group of actions simultaneously, without group of actions A impacting group of Actions B. For example, I might want that the decision making arout walking (finding a target, checking a path, walking forward, random walking if no target, etc) from the decision making around exaustion (food, energy level, speed, etc).
Is this indeed a limitation or is this a design choice? Are there any work arounds to that?

pliant crag
#

Honestly, I've started merging PRs to make both Actions and Scorers generic. I don't see why we couldn't make a Generic, too, and give it some kind of data field that folks could even just slap a PhantomData into just to distinguish them at the type level

pliant crag
#

that said, for your particular example: I think you don't want different thinkers here.

#

because different Thinkers would mean simultaneous Actions happening in parallel

#

so if you have decision making around going somewhere be separate from dealing with food, you can't make decisions about going-somewhere-and-getting-food as a single unit

#

it's weird to me for an entity to be taking multiple actions at the same time that are not defined together within the same thinker, and thus intentionally coordinated using, say, Concurrently

#

remember that your scorers are already running in parallel, but entities really tend to do one thing, or one "batch" of things at a time, not multiple completely unrelated and independent things that might potentially conflict with each other.

#

I find it hard to think of a good exception

#

the scorer/action "universes" would need to be essentially different and not really be able to step on each others' toes

#

the examples you give of "decision making about exhaustion" don't sound like things that belong in a thinker, btw, but in regular systems.

#

food levels are regular system. What belongs in the thinker is what action you take when you're hungry. Ditto energy levels.

last panther
#

I see

pliant crag
#

and action-when-you're-hungry is directly related to your pathing decisions

last panther
#

I think you touched the right point, I might be misusing it by lack of understanding

#

What kind of actions would you leave to the thinker then?

pliant crag
#

things that involve decisions based on general game state outside of the Thinker itself. You shouldn't use Scorers to store game state, for example.

#

Scorers should look at game state, and use that to come up with a Score

#

and Actions are a general state-machine-based async operation system, so you can use them for a lot tbh

#

you can even schedule one-off actions separate from Thinker decisions.

#

but in general, I'd say it goes like:
Regular Game State and systems that affect it (a dedicated system for your food levels going down, for example)
-> Scorers that take that game state and turn it into a 0.0..=1.0 score
-> Actions that don't know anything about Scorers themselves, but that execute arbitrary code as discrete, intentional actions that your entity is taking

#

so Actions are for things your entity does rather than things that happen to it in a sense.

#

the Thinker is the only place that knows to put Scorers and Actions together. That's how you get scorer/action reuse 🙂

#

oh, and Actions -> edit game state

last panther
#

I see, but for examples let's say that I have a chaser mob, that needs to find the closest target, wait for the path to be calculated and then start walking to the path and when it gets to a proper range it attacks (but keep chasing while attacking if the target is moving away and range is enough), and if there is no target just walk randomly im any dir.
Does that fit a single thinker in you opinion? Also, from those things, is thete anything wouldnt you put in the thinker?

#

Another thing I struggled with, combining thinking with cooldown (lets say that an entity shouldnt attack or walk more than once per second)

#

Is that something that should live outside the thinker?

#

Also, ideally scorers shouldnt change game state also, right?

#

So basically scorers should get game state and convert it to something measurable.
Actions consume and manipulate game state, but have no visibility of scorers.
Thinkers put up scorers and actions together by triggering actions based on scorers evaluation.
Is that a appropriate summary of what you just said?

#

Then thinking on cooldowns, they probably should live completely outside the thinker, be update (tick) on isolation and just be used either im the score (if you want high level cooldown e.g. if you want cooldown over the evaluation of a whole set of actions) or in the action level (if you want a low level cooldown e.g. if one of your actions should only happen once every second and you want to interrupt the propagation there).

#

Does it make sense?

pliant crag
last panther
#

Makes sense, in essence it does need to choose between attack or move

pliant crag
#

depending on the semantics you want to achieve

#

putting it on the scorer defers the decision, putting on the action defers the behavior, but locks in the decision

last panther
#

I see

#

Thanks a lot

pliant crag
#

the easiest decision for scorers is "yes/no", in which case they behave more like behavior trees. The Utility part of it comes from the fact that these can be ranges of values, and for that you need to some kind of concept, at the application level, of how much "influence" a certain bit of game state will generally have on your game

#

but you can tweak that stuff a lot to your needs

#

including configuring individual instances of scorers to use different multipliers/scales

#

just depends where it seems to fit best

#

measures and evaluators can be really interesting

last panther
#

Yeah I figured, it has a lot of possibilities, it's a quite nice concept. Im using it in very basic scenarios still, just understanding how it works for now

karmic geyser
#

How would you deal with allowing an action to "finish" if another action has scored higher.

In my example units can walk over to construction sites and work on them, takes maybe 10~ seconds at the moment.
There's another action that will slowly gain score over time which is to check if there are any available quests.
Sometime, halfway through construction the unit will have it's construction cancelled to check for quests, but that's not desirable. I'd much prefer the unit "waits until the construction action is done" then look for quests.

What are my options? Off the top of my head

  • Down-score looking for quests if construction is happening (seems too hacky, what if looking for quests is interupting other stuff?)
  • Up-score construction if it's already in progress to avoid things interupting it, aka hysteresis ?
  • Some kind of custom picker which will up-score things that are executing
unique copper
pliant crag
#

So if you don’t want to cancel right away, don’t set it to success/fail until you’re ready

#

But I recommend not trying to look at other actions. That should be defined in your thinker definition, at the data level

karmic geyser
# pliant crag The expectation is that scores interrupt ongoing actions. So the way you finish ...

Yea, that's what I'm struggling with. My goal is Sims style. So they'll choose something reasonable to do based on utility concepts, but unless there is a fire in the room/they need to go to work they won't suddenly drop what they're doing eg decide to exercise halfway through breakfast.

Up-scoring the current (hysteresis) action if it's in-progress seems like a good route to go for now, even if I do it on case-by-case. So if the unit is constructing it will be upscored while in progress, but if they're resting it wont.

Another idea that came to mind is to have a "unit state" enum like "In town"/"questing"/"shopping" that certain scorers would use to up/down weight themselves. kind of like in Sims they can get "inspired" or be in certain moods which affect their decision making.

karmic geyser
karmic geyser
#

If there a intended way to get the state/action of a thinker? Feels like the only options are a query on the Action entity, with Option<ActionA>, Option<ActionB> etc and check them until you find Some. Or using reflection thinker.get_field::<Option<Option<String>>>("current_action_label").

#

Is there a way to "nest" actions? I want to run cleanup logic that would apply if any of the actions in a Steps fail/succeed.

For example, if I'm in the Movement action of this sequence and get cancelled, there's no obvious way to do cleanup for the construction related state, eg release the "lease" on the in-progress building.

.when(
    construction::ConstructBuildingScorer,
    Steps::build()
        .step(construction::GetConstructionBuildingAction)
        .step(movement::MovementToEntityAction::default())
        .step(construction::ConstructBuildingAction::default()),
)
visual phoenix
#

@pliant crag Quick question, if I wanted to remove an entity from all Big Brain scorers and actions (i.e. clean up before deleting), how should I do that for example:

mut query: Query<(&Actor, &mut ActionState, &ChaseAndAttack)>,
#

Can I add an "Without <Active>" criteria here?

#

Also why is the Entity wrapped with Actor? I don't quite understand that

pliant crag
#

Actor is used to associate Actions with the entity actually performing the action

#

because Actions themselves are separate Entities

visual phoenix
#

Oh right that makes sense thanks

pliant crag
#

if you want to remove the actor itself, yes, you need to query for Actor and remove the contained entity id

visual phoenix
#

Well my ultimate goal is to stop the Big Brain systems on the specific Entity/Actor

#

Should I be removing the Actor is that unnecessary ?

pliant crag
#

ah I thought you just wanted to remove the actor itself

#

if you want to stop the Action from running on that actor, then you just finish the action as usual, by setting its state accordingly

visual phoenix
#

Im trying to avoid new actions from being added to entities in queue to be deleted

pliant crag
#

@visual phoenix then remove the thinker

#

the thinker component from those entities

#

no Thinker, no Actions, and the Thinker is an actual component on the actor itself.

visual phoenix
#

Thanks that makes sense

#

@pliant crag sorry dumb follow up question, how do I remove the thinker component, I don't understand what type it is using as I'm using the ThinkerBuilder:

                    .label("NPC Chase")
                    .picker(Highest)
                    .when(VisibleTargetScorer, ChaseAndAttack)```
#

or I mean which component

pliant crag
#

then the thinker will be removed

vagrant sundial
#

@pliant crag is there anything ready for bevy 0.15? otherwise, I could try to migrate it, if there isn't anything already in progress

vagrant sundial
pliant crag
#

Thanks. I haven’t done anything myself. I’ll take a look soon

visual phoenix
#

@pliant crag just curious on your suggestion. Currently I have a multi-step action:

Find a Drink, Move to Drink, Drink

If there are no Drinks available, should the Scorer being doing that check? Currently I have my Find a Drink action doing it

vagrant sundial
pliant crag
#

#crates message @vagrant sundial published 🙂

#

thanks

visual phoenix
#

@pliant crag I just realized this lines:

commands.entity(target.entity).remove::<ThinkerBuilder>();

Are producing these warnings, I believe the actions aren't being removed:

2024-12-01T00:02:09.050933Z DEBUG bevy_hierarchy::hierarchy: Failed to despawn entity Entity { index: 20, generation: 1 }```
#

Am I doing something wrong?

#

This is Bevy 0.14

pliant crag
#

I think that warning is because of them getting double-removed actually

visual phoenix
#

@pliant crag Right thanks, it doesn't cause any issues for me. I think its because I'm removing the ThinkerBuilder before despawning the Entity

pliant crag
#

Oh. Yeah you don’t need to do that yourself

visual phoenix
sonic hinge
#

Does anyone have experience with this crate? I'm strongly considering it for my project 🤔

#

Is it possible for some actors to push others?

Imagine I have a restaurant and one of the guests gets hungry, can they signal an employee to make food?

karmic geyser
#

Imagine I have a restaurant and one of the guests gets hungry, can they signal an employee to make food?
That sounds like something outside the realm of the library's responsibility. Your guest could perform an action like "order food" if there is an employee nearby, that order gets saved to a more general place, some kind of "order queue" (or blackboard, however you want to do it), then next time an employee is making food they would check in that place for orders.

#

A bit like bevy events 😛

pliant crag
#

+1 to the order queue

#

Restaurant workers can then have the queue (for example, how backed up it is) as part of their considerations

#

So if the queue only has one or two items but there’s 10 tables that still need their order taken, they won’t go deliver the order, or pick up a ready order, etc, because they have more pressing matters

karmic geyser
# sonic hinge Does anyone have experience with this crate? I'm strongly considering it for my ...

Does anyone have experience with this crate? I'm strongly considering it for my project 🤔
I think it's solid. It does utilise many entities to drive a single "agent", so if you have a very simple use-case it may seem very complex, especially if you're clicking around in egui inspector.

I made my own minimal version in one of my projects (titled galaxy brain lol), but as I wanted more complex scenarios it approached the big brain way more and more 😂 the main difference was I stored each possible action as a component rather than a child entity. It made much of the action query stuff easier as the action was on the same entity it would typically be querying, reducing the indirection in action logic. The downside though is you couldn't have multiple of the same type of action, so if you wanted to have "UseItem(X)" as an individual action, and not a general "use item" action that needed to consider all possible item usages, it broke down. There were some other minor downsides to my approach too but I can't remember them off the top of my head.

pliant crag
#

The main reason I didn’t go with components is that you can’t have duplicates that way

#

For example, you wouldn’t be able to have two separate FooScorers (in different parts of your thinker tree, with different parameters). For Actions, you’d think you can only do one at a time, but Thinkers themselves are actually actions, and they can nest, so you can actually have multiple thinkers running on a single entity heh

#

Tbh realizing that they had to be entities in order for utility AI to “work as intended” was my Big B Brain Big Insight. It was kind of mind bending to translate everything I was reading about the concept into ECS, because I couldn’t find anyone who had actually published info on how to do this

#

Also: imo, big-brain is very small and minimal, as far as the concept of utility AI goes. Any “real” use of the library would probably have to integrate its own action scheduling and queuing system with behavior closer to what the specific game needs

#

But bb is still a good substrate because it gives you the underlying fundamentals for the decision logic 🙂

#

It only really includes an action system because it needs something to interact with, at the Thinker level, but your actual action systems can basically just call out to your Real systems and that’s probably better

pliant crag
#

Take, for example, The Sims: considerations/scorers just trigger adding actions to a queue, which the player themself can manage (or even add to manually!). But I guess that system automatically removes things from its q based on scorer changes? Those could just be separate “Remove X” actions, though

karmic geyser
pliant crag
#

If you end up with “components as entities”, you’d literally end up with a system like bb

karmic geyser
#

True lol, I guess I was more thinking in the hierarchy sense. They could become components on the base entity as opposed to children.

karmic geyser
#

I wrote up the main differences I could remember when I made it (was a little while ago now 😅 ) https://gist.github.com/tbillington/1f89f519dbf060b03824ac801c811058
It ended up only around 300 lines, though it definitely has much less features than big brain!|
There's a vid and screenshot of some parts in use in bevy projects at the bottom

I didn't want to publish/promote it because I didn't know if the tradeoffs I made were good ones, so I'd still recommend big brain tbh if you're starting out 🙂

#

If be curious if you had any thoughts on how I did things kat, but ofc no pressure, I'm not actively using it anymore 🙂

pliant crag
#

Oooh I haven’t looked into how you do it but using hooks/observers for Action state sounds really interesting. Big Brain long predates those features

#

I would still want some kind of ActionState because even though it might feel tedious to match against everything like that (you don’t really have to, for simple ones!), being able to have bidirectional communication with the Thinker enables some pretty important (to me) features

#

For example, Failure tells composite actions to stop executing other steps

#

And Cancelled means that if the Thinker decides your currently running action is no longer a priority, you have the opportunity to do graceful shutdown

#

I feel like those would matter in practice a lot

#

I really like the idea of being able to do init/exec/finish/remove in one pass, but tbh you can do the first three at once in bb already and the last one wouldn’t have much of an affect that I can think of, since the action won’t be executed again anyway once you’ve finished

#

Not having to do double queries is really nice. I wish there were a way to do that in bb for sure tbh. Maybe some day as bevy evolves

#

One thing that I think Galaxy Brain makes harder is data-oriented definitions. I designed Big Brain such that you only needed to define the high level Scorers and Actions (and their systems), but all the logic about how they interact, how they’re picked, etc, is purely data based

#

Basically I designed big brain so that once bevy has a UI, it’ll be very easy to write a node-based editor for easily defining and composing Thinkers by just dragging and dropping generic behaviors around until they “feel” right, without having to touch a line of code

#

And you can’t really do that with a trait based system (although if you’re only programming by hand, traits can be very nice)

#

At one point I wanted to write the UI myself but tbh I think the Right Thing is to just wait for the bevy editor, and hopefully for someone to write a node linking system

#

Imagine being able to define your AIs the same way one might do Geometry Nodes or shaders in blender!

#

Anyway simplicity is fantastic when it’s enough and I think it’s good for folks to have the choice of things that fit their particular project. There’s gonna be things for which bb is too heavy, things it’s too small for, and things that will absolutely require a custom engine

#

One thing that’s crossed my mind a lot is the idea of big brain as a “model” engine that helps with prototyping and conveying the idea of how to even do utility AI, because it can be a bit of work to wrap your head around

#

And then you can move on from bb and write exactly what makes sense for your game. Because, as you’ve shown, you really really don’t need a lot of code to implement something like this, but if I want to grow it, you can do that basically ad infinitum

karmic geyser
#

Yea, hooks and observers give a lot more options. Required components could help maybe, but tbh they just feel like more convenient hooks/observers in this context 🙂

I didn't find the Thinker abstraction useful in my project, that's why there's isn't really an analogue in gb. I had other logic adding and removing scorers from my agents which fulfilled a similar role, so maybe if I didn't have that I would have implemented something similar. And yea, it was very code driven. The library was extracted from the project using it.

opportunity to do graceful shutdown
I could imagine this may be useful in a few scenarios, but I didn't run into any. I got away with having an on_remove hook for the action to do any cleanup. If that wasn't enough I could have made an "exit" action in the user level Composite action logic I guess 🤔

I really like the idea of being able to do init/exec/finish/remove in one pass
This was more of a interesting side effect of how I did things, not really a design goal. I could take it or leave it 🤷 I was tempted to code it so it would move into the next action if the current one finished but that felt a little too exotic and might generate surprising bugs xD

Yea, I also wish for node editing tools in the Bevy Editor™️ 😂 gb is definitely code focused atm. I guess it's almost like a low level utilities you would build upon rather than a "complete" solution.

But again, I only implemented what I needed for the project, and coded around the shortcomings!

sonic hinge
#

This has been interesting, thanks! I think I'm going to have to try if the crate suits me

#

As a programmer, I'd love to do everything myself, but as a game dev, I should be focusing in making the game, not writing code

wary vale
#

@pliant crag @karmic geyser I've also toyed around with a component-style utility AI library: https://github.com/msvbg/utilon. It does support multiple scores for the same type of action by allowing user-defined keys that go into a hidden hash map on Score<T> (for example: https://github.com/msvbg/utilon/blob/7e037e0670476656e3c9796284477347bc795aec/src/lib.rs#L627-L666)

It does feel more lightweight than big brain. I got a little annoyed by the entity indirection and how verbose it felt at times, but like Trent's library my library is also not especially battle-tested or polished. I think Trent and I have had similar goals with slightly different implementations.

It also explores having response curves that can be tweaked at runtime to change the score evaluation, and the use of reflection should mean that behaviors could be defined at runtime from a script. Needs some more iteration to be a good workflow though!

dapper oasis
#

@pliant crag hey I just wanted to ask, what does the system do if the entity has 2 needs at the moment? for example thirst and hunger, will it work on the 1 that appeared 1st, or is there some kind of scoring on whats more important?

pliant crag
#

You can choose a different picker depending on what you’d like the answer to be

dapper oasis
dapper oasis
pliant crag
#

The built in pickers should document their actual behavior pretty well, and you can just write your own pretty trivially if you want your own behavior

pliant crag
#

Instead of yes/no, you can define more intricate logic by fudging with numbers

dapper oasis
pliant crag
#

It’s not really “my” approach. Utility AI is pretty well established for a long time now. The only thing you could argue is “innovative” is that I adapted it to an ECS system. I looked and looked but found nothing about how to do this so I just rolled with what I could figure out lol

#

And it’s far from perfect 🙂

dapper oasis
#

i think its perfect :)

pliant crag
#

Please do check out the other utility libraries folks have come up with for bevy. Especially newer ones that take better advantage of newer bevy features

dapper oasis
#

ive been here fighting against rust (while coding my own ai) and then i found this beautiful library which does what i dreamt of

dapper oasis
pliant crag
#

It’s gently maintained. I occasionally do patches on it and I still accept patches and there’s folks in the community who make sure it’s always compatible with the latest bevy

#

But it’s largely “done”. You could just think of it as “stable”

#

Some day I might sit back down with it and do a rewrite that uses modern bevy features to address some of the known drawbacks (esp around performance and precision, neither of which big-brain is exceptionally good at)

dapper oasis
#

what is wrong with the precision?

#

i didnt even know utility ai was a thing

#

i just found out lol

pliant crag
#

You can’t really do frame precision with big brain. Not easily. It’s very concurrent and loosey goosey about frames

#

So if you need exact steps, things can get a bit tricky

#

Folks have still managed to it though I think

#

But most of the big-brain alternatives focus on this, or on performance

dapper oasis
pliant crag
#

It depends on your game 🙂

dapper oasis
#

im just currently reading into ai solutions do you think utility is better than goap? they seem quite similar?

pliant crag
#

Your game might be “tick” based, in which case every tick can be a very long thing, or it might be a game where players eventually reach frame-precision (like high level fighting game play)

#

I don’t think it’s a matter of what’s better than what

#

I like Utility because it’s pretty powerful and has sufficient complexity to do a lot of interesting things, but it’s very very data-oriented so it plays well with things like ECS systems where the gameplay is expected to be heavily defined by higher-level data configuration

#

Ditto behavior trees, but getting complex behaviors out of those is more work

#

But simple behavior is easier with behavior trees

#

You can get surprised a lot by utility AI because there can be so many things interacting in complex ways

dapper oasis
#

yeah thats true im already suprised by how suffisticated the ai can think with that system

#

i thought behaviour trees were good

#

but these are next level

pliant crag
#

I’m not remotely an AI expert but from what I’ve seen, GOAP seems to be used a lot when you want intricate behavior from “traditional” NPCs where you want to generally understand and define their behavior and priorities in an environment with limited complexity?

#

So, “normal” NPCs

dapper oasis
#

hmm i dont know either 😅 but i think utility is best for the most complex from what ive read

pliant crag
#

Stuff like Utility AI is what you see in games like The Sims or Dwarf Fortress, where a lot of the appeal of the AI is to behave in unexpected but sensible (or funny) ways based on interactions with a very very complex world

#

You don’t necessarily want regular NPCs in your narrative adventure game doing the kind of crazy shit you see in Dwarf Fortress, yeah?

#

I mean in the end I’m sure you can force any of these systems to behave essentially the same as far as a player is concerned

dapper oasis
#

yeah i think utility is a good choice

#

i just didnt know that even exist

#

is that new?

pliant crag
#

It’s at least 25 years old

dapper oasis
#

why are the yt videos only covering state machine, goap and b-trees? hmm

#

thats weird

pliant crag
#

I thiiiiink Maxis were the first ones to write about it (for The Sims)

dapper oasis
#

well now its time to implement my ai 😎 you told me earlier to look at other utility ai libraries, altough i only found this one? could you provide the names?

pliant crag
#

But there’s other non-utility-ai libraries

dapper oasis
#

am i blind, i dont see Thirsty, ever added in any system to the entity? in the sequence example?

dapper oasis
#

hello?

pliant crag
#

It’s there

dapper oasis
#

i did a text search but i might not be as good with github

#

i cant find it

dapper oasis
pliant crag
#

225

dapper oasis
#

not used

pliant crag
#

It’s used in line 270

dapper oasis
#

yeah but when is it ever added to an entity?

pliant crag
#

Never

#

Because that’s not how actions and scorers work

dapper oasis
#

is there a docs entry which describes that behaviour?

pliant crag
#

They are added to separate entities that are associated with the actor through an Actor component

#

The Actor component is a component on the Scorer entity itself, which is why you need to do that separate lookup to get the actor entity itself by id

dapper oasis
#

ah okay

pliant crag
#

That’s one of the reasons I mentioned perf stuff. If you have 10 scorers on an actor, that’s 10 more entities spawned, per actor

dapper oasis
#

can i manually add if the entity sees a player? cuz i dont think i can do that with a float or something?

#

so basically a see or not see

pliant crag
#

That’s the Thinker’s job

#

You can nest thinkers

dapper oasis
#

okay

pliant crag
#

Because Thinkers are actually Actions

#

So they can be used as the second argument to .when()

dapper oasis
#

Can there be multiple scores be associated with an action?

pliant crag
#

That’s what composite scorers are for

dapper oasis
#

the entity isnt moved to the player translation. what am i doing wrong?

pliant crag
#

You’re not actually using the GoToPlayer action

dapper oasis
#

oh i queried incorrectly?

pliant crag
#

You’re doing With<SeesPlayerScore> instead, which is never gonna have an ActionState

#

Try With<GoToPlayer>, I think

dapper oasis
#

now i see it too, thank you, i probably would have never found it 😅

pliant crag
#

🙂

dapper oasis
#

yeah it works

#

thanks :)

pliant crag
#

Yay nice

left canopy
#

hello

#

I am a new user of your crate

#

Just curious what ideally would be the new bee example?

lyric linden
#

Hi! Please help me to push my understanding of the library.
I see Steps and in my understanding it is “AND”. But I can't figure out how to get the “OR”.
I need something similar to Steps, but I need Thinker to take the next Action if the first Action fails. That is, I need at least one of.

lone dome
#

@lyric linden

lyric linden
lone dome
lyric linden
pliant crag
#

I would generally use a Picker for “OR” relationships

pliant crag
#

But Concurrently is good if you don’t mind executing several Actions at the same time

lyric linden
pliant crag
#

Just flip the logic appropriately

lyric linden
lyric linden
vagrant sundial
pliant crag
lyric linden
#

@lone dome , I did it! I used Steps as a basis, and I think I was able to convert it to OR. The first test seems to work. Thank you very much to you and @pliant crag for your help.

lone dome
lyric linden
#

Thank you anyway. 🙂

lyric linden
#

Hi all! Please help me to understand the problem with Thinker.
I have Steps with Step1, Step2, Step3. Step2 is the Thinker. The Thinker with FirstToScore { threshold: 0.5 } chooses between two actions Action1 and Action2.
The Scores for these two actions are configured in such a way that after Action1 is executed, Action2 will be executed. So their Scores look something like this: (0.6; 0.6) -> (Success; ---) -> (0.0; 0.6) -> (---; Success) -> (0.0; 0.0). But after completing this step, for some reason, Steps doesn't proceed to the next step. I looked through the code for Thinker, but I didn't see any state setting for Thinker, as it happens in Steps after executing some step. That is, I did not find a place where Thinker goes from Executing to Success or Failure. And after executing Action2, everything just freezes. Perhaps I need to somehow configure the behavior of the Thinker? I will be very grateful for your help.

lyric linden
#

In my situation that I described above, the Thinker stays in the Executing status. Can anyone tell me under what conditions can a Thinker switch to the Success or Failure state if it is one of the steps in Steps? Or the Thinker can be only one and at the top level?

visual phoenix
#

Here is my Thinker for my traveling Merchant:

#

And here are my Steps:

lyric linden
# visual phoenix Alex did you figure out your problem because it sounds like you may be confused ...

Hi Pete! If I understood correctly from my research, the Thinker should be a single one at the top level. And it cannot be used as an Action somewhere in the middle of the tree. I was confused by the fact that ThinkerBuilder implements the ActionBuilder trait and this allowed me to put it on a par with other steps in Steps. But it doesn't work, because in this case Thinker is always Executing and doesn't go to Success.
If to add to your example another Thinker as one more step, it will be similar to what I tried to do. I understand that I can change my tree a bit and get what I need. But I wanted to know if I understood correctly that the Thinker cannot be in the middle of the tree.

visual phoenix
pliant crag
#

Heads-up: I've moved big-brain over to codeberg, at https://codeberg.org/zkat/big-brain. I'm not finished with the migration yet (need to get CI going, republish to crates.io, set up a tombstone on github, etc), but I thought I'd give y'all an early warning that there will be no further github-side development.

visual phoenix
#

(My day job, we compete against Microsoft so I totally understand why 😂 )

pliant crag
#

Their intrusive copilot stuff and the react rewrite being bad

#

Basically

pliant crag
#

FYI: As part of a larger move away from GitHub, I've archived Big Brain on it and moved all future development over to Codeberg, at https://codeberg.org/zkat/big-brain

Please use that repo from now on.

Additionally: I am in the process of trying a major rewrite of the crate that I'm hoping will be much simpler, and much more efficient, thanks to features now available in recent version of Bevy! I'm very excited 🙂

silk marlin
silk marlin
pliant crag
silk marlin
#

😄 I thought that might be the case!

pliant crag
#

I need to see if observers can be stateful, though, or if they can only be functions

silk marlin
#

I don't think I've seen anyone do that, but it should all work

pliant crag
#

I need to find the actual docs for observers. I can't seem to figure out something that actually describes them and their usage. Just the examples lol

silk marlin
#

@tepid phoenix stateful observers is an interesting idea 🤔

silk marlin
pliant crag
#

oh the actual API docs do it

#

nice, thanks!

silk marlin
#

Yeah, I wrote a ton of docs for them last cycle 🙂

pliant crag
#

thank you so much!

#

this is super helpful

#

I just gotta bend my brain for a bit to figure out how all of this maps onto this AI model and then I should be good to go. I think the API is gonna be so much nicer after this, too.

#
#[derive(Debug, Clone, Copy, Component)]
pub struct Thirsty;

pub fn score_thirsty(event: On<DoScore, Thirsty>, thirsts: Query<&Thirst>, scorer: Query<&mut Score>) {
    if let (Ok(thirst), Ok(score)) = (thirsts.get(event.actor()), scorer.get(event.scorer())) {
        score.set(thirst.thirst)
    }
}

sketched out what I think the scorer side might be like

#

actions would be similar

pliant crag
#

fix'd

pliant crag
pliant crag
#

The full thirst example, with the new, sketched-out API:

#[derive(Debug, Default, Clone, Copy, Component)]
pub struct Thirsty;

#[scorer_for(Thirsty)]
fn score_thirsty(score: Score, thirsts: Query<&Thirst>) -> Score {
    score.update(thirsts.get(score.actor()).unwrap().thirst)
}

#[derive(Debug, Clone, Copy, Component)]
pub struct Drink { rate: f32, per: Duration }
impl Default for Drink {
    fn default() -> Self {
        Self { rate: 0.5, per: Duration::from_millis(500), }
    }
}

#[action_for(Drink)]
async fn drink_action(
    action: Action<Drink>,
    mut thirsts: Query<&mut Thirst>
) -> Result<(), ActionFailure> {
    while let Ok(mut thirst) = thirsts.get(action.actor()) && thirst.thirst > 10.0 {
        action.check_cancelled()?;
        thirst.thirst -= action.data().rate;
        action.sleep(action.data().per).await;
    }
    Ok(())
}

fn spawn_entity(cmd: &mut Commands) {
    cmd.spawn((
        Thirst(70.0, 2.0),
        Thinker::new()
            .picker(FirstToScore { threshold: 0.8 })
            .when<Thirsty, Drink>(),
    ));
}
pliant crag
#

Hey all, what do y'all think of this new API? I think it's doable with stuff that currently exists. Under the hood, this would all be based on observers, but this is what you would have to write yourself.

silk marlin
#

@here, since @pliant crag does not have the right, o they do not have the right

pliant crag
#

yeah I can't

#

I particularly like the idea of using rust async for actions from now on :)))

#

instead of manually managing a state machine

silk marlin
#

Your input on ideal APIs and mechanisms over in #1422021261655015564 would be welcome if you're interested

pliant crag
#

idk how easy that would be by "just" using a more general system

silk marlin
#

Yeah, checks out

pliant crag
#

(tweaked my sketch a bit more)

silk marlin
#

My hope is that we can provide some helpful building blocks, and this is a very "game-y" use case to help motivate the work there

pliant crag
#

yeah

#

I can imagine a similar system being useful for, say, manually doing animation work in a way that looks procedural??

#

that'd be kinda cool

#

I'm really excited about this. I've been really frustrated with big-brain's ergonomics for a very long time, and it didn't seem like there was any way to make it easier to work with, but recent stuff has been really compelling for fixing that

#

and I just needed idk... a bit of motivation and inspiration, and I this'll be a nice brief distraction

pliant crag
#

Another sketch, expanding a bit on how Thinkers would work now, and how the dynamic/static boundary might work in definitions:

fn spawn_entity(cmd: &mut Commands) {
    cmd.spawn((
        Thirst(70.0, 2.0),
        Thinker::build()
            .picker(FirstToScore { threshold: 0.8 })
            .when<Thirsty, Steps<(GoToWater, Drink)>()
            .when<Hungry, Eat>()
            .when_with(
                |_actor: Entity, _w: &World| Cond::new((Bored, Paranoid)),
                |_, _| Parallel::new(
                    (
                        Wander,
                        Thinker::build()
                            .when<EnemySpotted, Shoot>()
                            .when<FlowersSpotted, Steps<(WalkToFlowers, SmellFlowers)>()
                    )
                )
            )
    ));
}
#

basically, there's times when you want to define scorers and actions based on dynamic data. The most prominent example of this are Thinkers themselves, which are... actions!

#

I could set up a way to statically define Thinkers too, but I think that would end up being a much messier type-level API than just the builder pattern tbh

#

I could also split .when() into two methods, tbh, that are called sequentially...

#

oooh, how about this:

fn spawn_entity(cmd: &mut Commands) {
    cmd.spawn((
        Thirst(70.0, 2.0),
        Thinker::build()
            .picker(FirstToScore { threshold: 0.8 })
            .when<Thirsty>()
            .run<Steps<(GoToWater, Drink)>>()
            .when<Hungry>()
            .run<Eat>()
            .when<Or<(Bored, Paranoid)>>()
            .run_with(|_actor: Entity, _w: &World| Parallel::new((
                Wander,
                Thinker::build()
                    .when<EnemySpotted>()
                    .run<Shoot>()
                    .when<FlowersSpotted>()
                    .run<Steps<(WalkToFlowers, PickAFlower, SmellFlower)>>()
            )))
    ));
}
pliant crag
#

here's a KDL-based sketch, since I've been designing Big Brain from the get-go so that it's data-oriented. This could just as well be a graphical node editor where you drag little lines between inputs and outputs

pliant crag
pliant crag
#

Probably better like this:

Thinker {
    picker FirstToScore threshold=0.8
    when Thirsty
    run {
        GoToWater
        Drink
    }
    when Hungry
    run {
        Eat
    }
    when Or {
        Bored
        Paranoid
    }
    run {
        Thinker {
            when EnemySpotted
            run {
                ShootEnemy
            }
            when FlowersSpotted
            run {
                WalkToFlowers
                PickAFlower
                SmellFlower
            }
            otherwise {
                Parallel {
                    Wander
                    HumATune
                }
            }
        }
    }
}
pliant crag
#

I’ve started implementing this sketch 🙂

#

I’m excited

tepid phoenix
#

In my goal framework for panoply, I distinguish between two kinds of sequential subgoals: sequence and contingent. The difference is that the latter backtracks, but the former does not.

  • When a subgoal in a sequence completes, it proceeds to the next subgoal and never backtracks.
  • In a contingent, if at any point any previous goal is no longer satisfied, it backtracks and works on that previous goal first.
    So for example, if the character wants to cook something, first they have to turn on the stove before cooking the food; but if someone later turns off the stove while the food is cooking, they will turn the stove back on before continuing.
#

An example of a non-contingent goal is a patrol route: you don't want to re-visit the waypoints once you have visited them, that subgoal is considered complete even though you are no longer in proximity of that waypoint.

pliant crag
#

Hmmmm

pliant crag
#

So each patrol waypoint might have a last_visited

#

And that causes a score to go up three more time goes by

#

Though that’s a little roundabout. Tbh I would just implement it as an async loop that goes between waypoints until the action is interrupted by other priorities

#

So you’d just write a whole loop with await points in it, check for cancellation, and that’ll work fine if the Thinker decides to task switch because, say, an event was spotted or your patroller got hungry

pliant crag
#

Hah yes I am totally wiring my own async runtime I guess

#

Is surprisingly easy, though, if all you want is to self manage polling

unborn flax
#

When I was testing thigns out... ten years ago. I ended up on two scenarios that pretty much defined wether or not what I was doing would force me to leak abstraction or not.
One of them I borrowed from some guy posting on Stackoverflow
The other one... isn't necessary for all games

First problem (This was fun to solve with a behaviour Tree):

Guard Problem
Guard on patrol, finishes his patrol sequences and starts on his sitting down idle sequence, which has an animation.
The Behaviour Tree node running this, has to run an animation so it's a straight Running -> Success transition for the leaf.
Guard is responsible for security.
Larm goes off.
In the middle of animation one of his conditions who would normally be triggered, will not because his current node/action/leaf is under RUNNING because of a slow animation. He will have to sit down, before he will react to the larm

Second problem, and this isn't necessary for most games, but it is an interesting problem, because Mark Dave who loves Utiltiy AI made a claim that it can do everything.

NPC has a temperature and it is falling and he is getting cold.
Getting near fire would warm him up
NPC can build campfire, if he had wood
NPC can get wood, if he had an axe
NPC can get an axe, if he could equip.

The problem with Utility AI here would be, how the HELL would you design build campfire or all its requirement.
You'd have to hard code allmost all scenarios that can occur under that particular action, which makes it messy

#

From intial glance, your new design looks a whole lot like a behaviour tree

pliant crag
#

? I guess I'm not sure why the latter example would be so hard in Utility AI

unborn flax
#

Ah

#

I think.. you have to imagine it.
Like how would the code look like

pliant crag
#

gimme a minute to sketch it out

#

in data, since that's how you'd actually define it, assuming you have scorers and actions written (the building blocks, which are the only thing you need to "hard code")

pliant crag
#

ah, I made it more complicated for myself than it needed to be

#

Something like this, maybe?

Thinker label=campfire_example {
    when BodyTemp threshold=35ºC
    run {
        Thinker label=cozy_by_fire {
            when FireNearby
            run GetCozy
            otherwise {
                Thinker label=build_fire {
                    when HasResource=wood
                    run BuildFire
                    otherwise {
                        Thinker label=get_wood {
                            when And {
                                TreesNearby
                                HasItemEquipped type=axe
                            }
                            run {
                                GoToNearest type=tree
                                CollectFrom type=tree
                            }
                            when NeedsItemEquipped type=axe
                            run {
                                FindItem type=axe
                                EquipItem type=axe
                            }
                            otherwise Panic reason="You're getting too cold..."
                        }
                    }
                }
            }
        }
    }
    otherwise Idle
}
#

this is just data, btw, not "hard coded". Those CamelCase things are the only bits that involve rust code. This text could just as well be a node editor-managed thing, a bunch of little files linked together, etc. It's pure data.

#

and is meant to be "scripted" by non-programmers

#

this is, btw, conceptually possible with big-brain's current version, too, it's just a bit of a pain to encode because big-brain's ergonomics kinda suck (the big thing I'm trying to even fix)

#

@unborn flax but Utility AI itself is... basically like behavior trees with a planner built in I guess? idk the terminology too well 🙂

#

if you read the above carefully, one might imagine some opportunities where you could even flatten that tree out and just lean harder on scorers to "order" things for you, but I also like nesting thinkers sometimes

#

big-brain has a very similar concept to behavior trees/GOAP where action success/failure determines whether actions are able to "chain together"

unborn flax
# pliant crag <@255391895671406593> but Utility AI itself is... basically like behavior trees ...

Nah, it doesn’t have a planner. It’s just very reactive. It goes through all possible things that can occur given the conditions, they all have a score. Sometimes that’s a fixed score and other times it’s a score dependent on some world stage. Sometimes they take that number and make it fuzzy but that’s not important.

Anyway the lowest score wins and it will perform that action until reactivity kicks in. It’ll keep doing that until a new action wins. In order to prevent jittery that it jumps between actions since it runs all the time, the current action gets extra points for the sorting before picking the next action, this is to have some stickyness to decisions

That’s basically it. To translate to bevy, a bunch of systems run, check conditions if it’s viable and if so, return with score

Final system sorts and pops and as the current action is occurring, it’s still doing the scoring for the next, so you never have stale decisions.

No plans, no stacks, just a single action of the lowest cost gets chosen.

pliant crag
#

that's... not quite the whole story

#

since you can switch the picker

unborn flax
pliant crag
#

and picking can be done in whatever order you want it to happen

#

including "just pick the highest, regardless of order"

#

@unborn flax you could reuse that get_wood thinker wherever you like, yes

unborn flax
unborn flax
pliant crag
#

in big-brain parlance, the "picker" is the thing that looks at all the choice scores, and decides which of them to execute

unborn flax
#

I mean the whole concept is that it’s a flat list of actions associated with a score and you need to sort the list so you can pic the highest/lowest.

#

I was talking about utility ai.

pliant crag
#

so if you have 3 choices, and they scored 0.3, 0.5, and 0.9, and you use picker FirstToScore threshold=0.5, you'll pick #2, but if you use picker Highest, you'll pick #3

#

the thing about big-brain is that Thinkers themselves are actions

#

that's why you can nest them like that

#

also, regarding your concerns about animation cancellation: actions in big-brain are cancellable. When a higher-priority thing wins in the middle of something "RUNNING", big-brain sends a signal to that running action

unborn flax
#

Sure, I wasn’t explaining how your idea works 😂. I

#

Sorry if it was unclear.

pliant crag
#

and the action is able to then wind down whatever it's doing in the most appropriate way

#

for example, in your case, the animation would "rewind" quickly, before the action finally yielded and completed

#

so not only is the sitting animation cancelled, but you look like you're standing back up fast

#

which I think is what you'd want in this case

unborn flax
pliant crag
#

you don't wanna just cancel and teleport to standing

pliant crag
#

in Big Brain, cancellation is well defined, yes, and actions do not stop running on cancellation until you explicitly handle the cancellation ActionState

unborn flax
#

The way we did it was certain nodes were reversible. And if they were reversible they can get cancelled

pliant crag
#

and turn it into either ActionState::Success, or ActionState::Failed, or you just keep it in ActionState::Running

#

(or Cancelled)

unborn flax
#

Yeah.

#

So this a whole lot like a BT

pliant crag
#

it is, yes

unborn flax
#

It’s a predefined graph.

pliant crag
#

but with like... values

unborn flax
#

Yeah I mean you could have values in BT too. Your condition nodes (forgot what they we called) could fail on. The value.

#

So you’ll have many thinkers for an NPC with different labels?

pliant crag
#

you could, sure

#

(current big-brain doesn't let you do that. But I'm fixing that)

unborn flax
# pliant crag but with like... values

Regarding the scoring do you score after conditions have been met and it needs to be sorted by others who also scored or does the score decided if we go into this branch at all?

pliant crag
#

big-brain currently continuously scores all conditions that might actively be of interest

#

all the way up the tree

#

this is needed because of, well, that's how cancellation even works

#

any place anywhere up the tree can decide to cancel an entire running subtree so

#

big-brain is... not great at giving you control to tune how often this runs and how much load it's putting on your system right now, unfortunately. Not right now. Another thing this rewrite should be able to fix, and part of the reason I'm looking at either manual ticking or events

#

because honestly, you don't really need to check every condition on every frame. That's just plain not realistic for 90% of NPC scenarios

#

realistically speaking, the highest resolution your NPCs would care to measure conditions on is like 4fps 🙂

unborn flax
pliant crag
#

(150-250ms, most people's reaction times)

#

sure! I mean, as it should, imo, if you want the kind of deep simulation where new things will draw your attention away when they're higher-priority, right?

#

I personally find it pretty intuitive but shrug idk

unborn flax
#

He problem is this.

#

Do you wanna babysit conditions written in English

#

Or do you wanna babysit conditions that have esoteric numbers

#

BT picked English utility picked numbers.

pliant crag
#

well, BT picked booleans, and Utility picked ranges

unborn flax
unborn flax
#

They have names.

pliant crag
#

how is that different from what I just wrote out for you?

#

all the conditions have names

unborn flax
#

No I’m not saying it is.

#

If anything the structure and reading patterns reminds me a lot of bts

#

You have a reading structure on this, it’s top down left to right?

pliant crag
#

yes, it does tbh! I think they're basically the same, except that when you're working with numbers already, Utility AI becomes WAY more powerful

unborn flax
#

Double edged swords

pliant crag
#

so at the most basic level, if all you're thinking of is in terms of pure conditions, having 0 or 1 as your utility numbers is sufficient

unborn flax
#

You mean between 0 and 1?

#

Or strict 0 or 1?

pliant crag
#

no literally either 0 or 1

#

that's a boolean, done

unborn flax
#

Yesh

pliant crag
#

but!

#

when you have actual values, which is what Utility AI was designed for (The Sims, etc), when you're weighing different values against one another, that's not a thing you can really do very easily in BTs by comparison

#

especially when the values are on different local scales

#

and yes, the jitteriness thing is a known thing! But its solution is also well known: you make your actions "sticky" until a large enough threshold is reached

unborn flax
#

Yeah because Utility scored at the end.

pliant crag
#

so for example, you don't keep eating until you get canceled: you keep eating until you're "full"

unborn flax
#

BT scores at the beginning

#

Hmmm

pliant crag
#

that leaves the decision more to the action though

unborn flax
#

I wonder if you sorta could… abuse the blackboard.

pliant crag
#

but that's one thing you can do

unborn flax
#

You know you could easily build a ui for your structure in vscode.

pliant crag
#

you can make your scorers themselves sticky, though

#

so for example, they can be stateful and increase up to a certain threshold

unborn flax
#

Do you prefer drag and drop or structure text?

pliant crag
#

and when they hit, say, 0.8, they will stay there, until they see a value hit a certain lower threshold

#

and then you've got stickiness built right into your scorers

#

I'm designing for UI, yes!

#

it's been a huge goal of mine to eventually have one for big brain

#

literally a drag and drop, node-based thing

unborn flax
#

Hmm.

pliant crag
#

like Blender's Geometry Nodes

unborn flax
#

Make it attach at runtime and colour the running leafs green and red for failed

#

Could be cool.

pliant crag
#

yeap 🙂

#

I'm excited for the bevy editor because it could support a plugin for this, in theory

unborn flax
#

Ever since I started using obsidian I’ve loved structured text more.

#

As long as the editor controls the flow.

Like moving up and down will take me to the elements and not just move a cursor.

#

And strong autocomplete.

pliant crag
#

indeed

unborn flax
#

Anyways I gotta bail. It’s was fun.

pliant crag
#

KDL can do that 😉

unborn flax
#

Ping me when you got something to try or just wanna discus d.

pliant crag
#

cheers

#

yeah, I'm working on a different project that took priority again, but I plan on getting back to this soon

visual phoenix
#

@pliant crag I'm excited you are looking to rewrite big-brain! I am heavily leveraging it currently and I agree the ergonomics have caused me a lot of pain

#

Here are my current villager Thinkers

                .label("Villager")
                .picker(Highest)
                .when(EnemyDistanceScorer, Flee)
                .when(ThirstyScorer, find_move_to_and_drink)
                .when(HungryScorer, find_move_to_and_eat)
                .when(DrowsyScorer, find_move_to_and_sleep)
                .when(ExhaustedScorer, Sleep)
                .when(HeatScorer, find_move_to_and_shelter)
                .when(CapacityScorer, UnloadItems)
                .when(
                    IdleScorer,
                    Idle {
                        start_time: 0,
                        duration: 100,
                    },
                )
                .when(GoodMorale, ProcessOrder),
        ))```
unborn flax
unborn flax
#

@pliant crag hey sorry to bother you. I never did ask what your litmus test or benchmark is. Or what you thought about the exercise we did.

I often try to think of like edge cases. I like the concept of a designed leaky abstraction, where I can bail out of the standard programming temporarily and return without too much code or runtime impact.

Rather than be too stringent and hope all edge cases are covered.

Is there a particular scenario you find jarring or takes your ideas to the brim? Would be cool to make a sample scenario together.

pliant crag
#

honestly? I haven't put a ton of thought into big-brain in a few years