ppb / pursuedpybear

A python game engine.
https://ppb.dev/
Artistic License 2.0
259 stars 98 forks source link

[Feature] Finite State Machine #385

Open AstraLuma opened 4 years ago

AstraLuma commented 4 years ago

Finite State Machines (FSMs) are a useful building block for behavior swapping and basic AI.

We should have it as a feature.

AstraLuma commented 4 years ago

An idea:

class BaconAndEggs(FSM):
    def eggs(self, num_of_spam):
        ...
    def spam(self, num_of_spam):
        ...

bas = BaconAndSpam()
bas(8)

This would be based on/borrow from Enum.

@fsm_swapped(BaconAndEggs)  # Can also be an instance, for a "global" FSM
class Spam:
    class State_eggs:
        def foo(self): return "eggs"
    class State_bacon:
        def foo(self): return "bacon"

Spam().foo()

This naturally applies to game objects and their event handlers.

This is great for global FSM instances, but bad for private ones (per-sprite or per-scene)--especially if you want to allow multiple (non-conflicting) FSMs to hook the same class.

One way to resolve this might be a property descriptor:

class Spam:
    state = my_fsm(BaconAndEggs)  # Allows access to the implicit local FSM, name only needed if you have more than one

"Implicit local FSM" feels pretty magical, though? But it does allow for simple declarations like above. I think I prefer it to more explicit FSM hooking with a lot of decorators? That would be:

@fsm_swapped
class Spam:
    state = BaconAndEggs.property()

    @state.eggs
    class state:
        def foo(self): return "eggs"

    @state.bacon
    class state:
        def foo(self): return "bacon"

Spam().foo()

Which is potentially more comprehensible in how it's all wired together, in exchange it's more verbose. The real question is what version is better for students and teaching.