#Help with State Machines - Need to Pass Around Mutable Lists of States

1 messages · Page 1 of 1 (latest)

scenic oak
#

Hi! I'm working on implementing a pair of state machines to help control ants in my game. The two state machines are AntBehaviorMachine, which models discrete behaviors for the ants (wandering, approaching, harvesting, etc), and PheromoneMachine, which models how an ant chooses behaviors (Scouting = wandering and approaching the colony, but Harvesting = approaching the target then approaching the colony). I've run into a small speed bump that I can't search engine my way out of. Ants have a Caste which is partially defined by the pheromone(s) that Caste obeys (do they Scout, do they Harvest, do they Fight, etc), and these pheromones should be chosen by the player (ie, the player can change the Pheromones a Caste obeys). The State Machine pattern has you initialize instances of the State interface (eg IPheromone or Scout) for each entity implementing the State Machine (eg PheromoneMachine). So an Ant has a PheromoneMachine which initializes instances of Scout, Harvest, etc. The problem I'm running into is: how do I define a Caste of Ants with its PheromoneSequence but also have the elements of each PheromoneSequence be instantiated for each ant? Is the best solution to have the Caste keep track of a List<PheromoneEnum> or something like that, and the Ant/PheromoneMachine "decodes" the enum into the proper instance of the IPheromone?

#

I know it's complicated. I can share all of my code but it's a lot of code right now. I'm trying to make a flexible system to model 1) different behaviors for the ants 2) different behavior patterns for ants. I settled (so far) on a sort of dual State Machine. Idk if that's the right move.

quiet pagoda
#

I actually just did something similar to this, maybe I can help but no promises.

scenic oak
#

If you beat me to my game I will find you and wipe your harddrive

#

jk

quiet pagoda
#

wasn't for a game so dw, just a project.

#

so what is Caste? Having a hard time reading the big block of text.

scenic oak
#

ok let me try to describe it:
Ant: individual entities in the game that behave autonomously
Caste: data that a group of ants share, including a List<Pheromone> (<<< this is my problem right now), which defines the behavior pattern for the ants
AntBehaviorMachine / IAntBehavior: concrete behaviors for ants
PheromoneMachine / IPheromone: a behavior pattern for an ant (which can be strung together with other IPheromones to make a List<IPheromone> or a so-called PheromoneSequence

quiet pagoda
#

and your problem is that the ants are sharing the list?

scenic oak
#

The Caste's pheromone sequence should be mutable, defined by the player. But I can't store a List<IPheromone> on the Caste, because each ant should have its own instance of each IPheromone

#

No, the problem is really with types. Ideally, I could pass around something like List<Type(IPheromone)> which is like references to the different Pheromone objects, like Scout etc.

quiet pagoda
#

I think im starting to understand.

scenic oak
#

Like I don't want the Caste to store instances of the Pheromones, just references so that the ant can instantiate its own.

#

bc I think (??) ants need their own instances for the state machine to work. Or at least that's what archetypical State Machine pattern says.

#

I think I'll just hack it with enums and switches.

quiet pagoda
#

I believe if you use generics it may work

scenic oak
#

Say more

quiet pagoda
#

Perhaps make a new class, idk something like PheramoneReference<T> where T : IPheromone, and in that class have a method to instantiate a T?

#

Then when the ant needs to grab a pheramone, it grabs an instance of it without Caste storing instances?

#

if I'm understanding correctly that's what you want right?

scenic oak
#

Well, an instance of a pheromone needs the ant object that instantiates it.

quiet pagoda
#

so:

scenic oak
#

Like I mean _scout = new Scout(this)

#

For the Scout pheromone, for example

quiet pagoda
#
public class PheramoneReference<T> where T : Pheremone {

  public T GetInstance(Ant instantiator) => new (instantiator);
}
```?
#

so in that case it'd be better to make a base class of pheromone where the constructor takes an ant

#

are your constructors of different signatures? or have to be instantiated at a certain time?

scenic oak
#

woof, we're beyond me now

#

I don't mess with this T stuff and idk anything about signatures. lol

quiet pagoda
#

what is Pheremone?

#

is it just a normal class?

scenic oak
#

Currently there's just IPheromone, an interface that specific pheromones inherit, like Scout

quiet pagoda
#

do all pheromones have the same constructor?

scenic oak
#

So that the AntBehaviorMachine and the PheromoneMachine can talk to each other and decide how to change behavior.

#

No

#
public interface IPheromone
{
    public void Start();

    public void Update();

    public void Finish();
}
#

I haven't written an interface version of Scout yet

quiet pagoda
#

Why does the Scout pheromone need an ant reference? To move the ant?

scenic oak
#

Bc I'm converting things as we speak. Ran into the problem of defining the pheromone sequence for the caste

#

Yes, or for example, I want to store things like progress on an ant's specific Scout or Harvest pheromone

#

So like if an ant needs to circle a target for like 5 seconds, that's a pheromone-level behavior pattern.

#

So at the very least, an ant needs a unique instance of its own pheromones.

quiet pagoda
#

In your interface's Start method can you take an Ant parameter, that way you can get all the information you need from the ant when it calls start? That way the constructor can be parameterless?

#

You can also add a public bool Valid(Ant ant); and then do something like this:

scenic oak
#

Oh I misled you

#
public class PheromoneMachine
{
    public IPheromone CurrentPheromone { get; private set; }

    private List<IPheromone> _pheromoneSequence;
    private int _currentPheromoneIndex;

    private Scout _scout;
    public Scout Scout => _scout;

    private TendColony _tendColony;
    public TendColony TendColony => _tendColony;

    private Collect _collect;
    public Collect Collect => _collect;

    public event Action<IPheromone> PheromoneChanged;

    public PheromoneMachine(Ant ant)
    {
        _scout = new Scout(ant);
        _tendColony = new TendColony(ant);
        _collect = new Collect(ant);
    }

    public void Initialize(List<IPheromone> pheromones)
    {
        _pheromoneSequence = pheromones;

        CurrentPheromone = pheromones[0];

        CurrentPheromone.Start();

        PheromoneChanged?.Invoke(CurrentPheromone);
    }

    public void NextPheromone()
    {
        _currentPheromoneIndex++;

        CurrentPheromone.Finish();
        CurrentPheromone = _pheromoneSequence[_currentPheromoneIndex];
        CurrentPheromone.Start();

        PheromoneChanged?.Invoke(CurrentPheromone);
    }

    public void Update()
    {
        if (CurrentPheromone != null)
        {
            CurrentPheromone.Update();
        }
    }
#

That's the PheromoneMachine which is what actually needs the sequence and, as you can see, stores the specific instances of the pheromones.

quiet pagoda
#

some of your pheremones still needs an ant reference right?

scenic oak
#

Maybe. All I'm nearly sure of is that each ant needs it's own PheromoneMachine with its own instances of all the pheromones.

#

But simultaneously, an ant's PheromoneSequence should be mutable.

#

As written, this machine won't work since _pheromoneSequence is itself a list of IPheromones and the machine instantiates its own IPheromones

#

I think the easiest workaround is to give the machine a list of pheromone enums and have it decode those with a switch to decide IPheromone to instantiate.

#

But idk if that's good practice. I have to make sure that my pheromone enum is in agreement with the pheromones available.

#

Idk I'm so novice. lol

quiet pagoda
#

I don't think so. If you could store a list of all pheromones somewhere you could do this:

public class PheremoneStorer {
  public List<IPheromone> all;

  public T GetPheromone<T>(T pheromone, Ant ant) : where T IPheromone {
    List<IPheromone> valids = all.FindAll(p => p is T && p.IsValid(ant)); // pseudo code, not sure if this exactly will work
    // at this point this will give you all the pheramones that can be used with that ant
  }
}
```Perhaps you could just get what pheromones of type T are good to be used by that ant?
scenic oak
#

Hm

quiet pagoda
#

or if you expect to only have 1 type of each pheremone in the all list

#
public class PheremoneStorer {
  public List<IPheromone> all;

  public T GetPheromone<T>(T pheromone, Ant ant) : where T IPheromone {
    return all.Find(p => p is T && p.IsValid(ant)); // pseudo code, not sure if this exactly will work
  }
}
#

so that way the pheremones are just spererate entirely? You could define a copy method in the pheromone and do this:

#
public class PheremoneStorer {
  public List<IPheromone> all;

  public T GetPheromone<T>(T pheromone, Ant ant) : where T IPheromone {
    return all.Find(p => p is T && p.IsValid(ant)).ReturnCopy(ant); // pseudo code, not sure if this exactly will work
  }
}
scenic oak
#

oooo, a copy method is a great idea

#

in fact ,a copy method is the solution, I think.

quiet pagoda
#
public interface IPheromone
{
    public void Start();

    public void Update();

    public void Finish();

    public bool IsValid(Ant ant);

    public IPheromone ReturnCopy(Ant ant);
}
scenic oak
#

So a Caste has a list of the Pheromones the Caste will obey, and when the ant's PheromoneMachine wants to instantiate its own pheromones, it will just ask the Caste's list for a copy.

#

That's perfect!!

quiet pagoda
scenic oak
#

Sure, sure.

#

Many thanks, stranger.

quiet pagoda
#

real quick

#

if it doesn't and you want to go the enum route

#

something like this could work easily

scenic oak
#

And the enum solution gets unwieldy under that constraint.

quiet pagoda
#
public interface IPheromone
{
    public void Start();

    public void Update();

    public void Finish();

    public PheromoneType { get; } // I think u can do properties here? If not, just make it a method.
}
```That way you can just require your pheremones to define what type it is.
#

well best of luck. I know this kind of stuff is really hard, I spent 2 months on something similar and had way less progress than I wanted haha.

scenic oak
#

tough stuff!!! but this is kind of perfectly on the edge of my ability, so I'm loving it.

#

It's almost out of my ability, and I wanted to give up earlier. But we push right through that!

quiet pagoda
#

good luck soldier 🫡